1# TCP client/server API for C++ (with SSL/TLS support) 2[](http://opensource.org/licenses/MIT) 3 4 5## About 6This is a simple TCP server/client for C++. Under Windows, it wraps WinSock and under Linux it wraps 7the related socket API (BSD compatible). It wraps also OpenSSL to create secure client/server sockets. 8 9It is meant to be a portable and easy-to-use API to create a TCP server or client with or without SSL/TLS 10support. 11 12Upcoming features : using the sockets in an async way and proxy support. 13 14Compilation has been tested with: 15- GCC 5.4.0 (GNU/Linux Ubuntu 16.04 LTS) 16- Microsoft Visual Studio 2015 (Windows 10) 17 18Windows Users : vcpkg (Microsoft C++ Library Manager) can be used to easily install OpenSSL and generate the Visual Studio solution with CMake. With vcpkg, no need to manually copy the DLL in the output directory, vcpkg handles all that ! Look at "Building under Windows via Visual Studio" section, for instructions. 19 20## Usage 21Create a server or client object and provide to its constructor a callable object (for log printing) 22having this signature : 23 24```cpp 25void(const std::string&) 26``` 27 28For now, you can disable log printing by choosing the flag ASocket::SettingsFlag::NO_FLAGS when creating 29a socket or by providing a callable object that does nothing with the string message. 30 31```cpp 32#include "TCPClient.h" 33#include "TCPServer.h" 34#include "TCPSSLServer.h" 35#include "TCPSSLClient.h" 36 37auto LogPrinter = [](const std::string& strLogMsg) { std::cout << strLogMsg << std::endl; } 38 39CTCPClient TCPClient(LogPrinter); // creates a TCP client 40CTCPServer TCPServer(LogPrinter, "12345"); // creates a TCP server to listen on port 12345 41 42CTCPSSLClient SecureTCPClient(LogPrinter); // creates an SSL/TLS TCP client 43CTCPSSLServer SecureTCPSSLServer(LogPrinter, "4242"); // creates an SSL/TLS TCP server to listen on port 4242 44``` 45 46Please note that the constructor of CTCPServer or the SSL/TLS version throws only an exception in the Windows 47version when the address resolution fails, so you should use the try catch block in this particular context. 48 49To listen for an incoming TCP connection : 50 51```cpp 52ASocket::Socket ConnectedClient; // socket of the connected client, we can have a vector of them for example. 53/* blocking call, should return true if the accept is OK, ConnectedClient should also be a valid socket 54number */ 55m_pTCPServer->Listen(ConnectedClient); 56``` 57 58A wait period (in milliseconds) can be set, to avoid waiting indefinitely for a client : 59 60```cpp 61m_pTCPServer->Listen(ConnectedClient, 1000); // waits for 1 second. Will return true, if a client connected to the server 62``` 63 64To connect to a particular server (e.g 127.0.0.1:669) 65 66```cpp 67m_pTCPClient->Connect("127.0.0.1", "669"); // should return true if the connection succeeds 68``` 69 70To send/receive data to/from a client : 71 72```cpp 73const std::string strSendData = "Hello World !"; 74m_pTCPServer->Send(ConnectedClient, strSendData); 75/* or */ 76m_pTCPServer->Send(ConnectedClient, strSendData.c_str(), 13); 77/* or even an std::vector<char> as a second parameter */ 78 79char szRcvBuffer[14] = {}; 80m_pTCPServer->Receive(ConnectedClient, szRcvBuffer, 13); 81``` 82 83To send/receive data to/from a server : 84 85```cpp 86const std::string strSendData = "Hello World !"; 87m_pTCPClient->Send(strSendData); 88/* or */ 89m_pTCPClient->Send(strSendData.c_str(), 13); 90/* or even an std::vector<char> */ 91 92char szRcvBuffer[14] = {}; 93m_pTCPClient->Receive(szRcvBuffer, 13); 94``` 95 96To disconnect from server or client side : 97 98```cpp 99m_pTCPClient->Disconnect(); 100 101m_pTCPServer->Disconnect(ConnectedClient); 102``` 103 104A client socket can be polled to ensure that a receive operation won't block indefinitely if a timeout has not been defined : 105 106```cpp 107// client 108int ret = ASocket::SelectSocket(tcpClient->GetSocketDescriptor(), 300); // poll for 300 ms 109if (ret > 0) 110{ 111 int readCount = m_pTCPClient->Receive(RcvBuffer.data() + readBytes, chunkSize); 112 113 //... 114} 115 116// client socket managed by a server 117int ret = ASocket::SelectSocket(ConnectedClient, 50); 118``` 119 120Or you can define a recevive (or send) timeout value : 121 122```cpp 123ASSERT_TRUE(m_pTCPClient->SetRcvTimeout(250)); 124 125m_pTCPServer->SetRcvTimeout(ConnectedClient, 250); 126 127// Set timeout value to zero to disable timeout 128``` 129 130Before using SSL/TLS secured classes, compile both library and the test program with the preprocessor macro OPENSSL. 131If you don't want to compile secure classes, you can indicate that to CMake when generating a makefile or Visual Studio solutions, by setting SOCKET_CPP_BUILD_WITHOUT_SECURE_CLASSES=TRUE (under Windows, in CMake-GUI, add the entry, select "BOOL" and check "Value") : 132 133```shell 134cmake -DCMAKE_BUILD_TYPE=Release -DSOCKET_CPP_BUILD_WITHOUT_SECURE_CLASSES etc... 135``` 136 137Almost all the operations look similar to the operations above for unencrypted communications, the differences are : 138 139The client socket to provide to the Listen method of an CTCPSSLServer is of type ASecureSocket::SSLSocket. 140```cpp 141ASecureSocket::SSLSocket ConnectedClient; 142``` 143 144Before listenning for incoming SSL/TLS connections, you have to set the server's certificate and private key paths via 145the proper methods : 146 147```cpp 148m_pSSLTCPServer->SetSSLCertFile(SSL_CERT_FILE); 149m_pSSLTCPServer->SetSSLKeyFile(SSL_KEY_FILE); 150``` 151 152You can also set CA file if you want. Otherwise, for now, passphrase must be included in the private key file. 153 154To create SSL test files, you can use this command : 155 156```Shell 157openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem 158``` 159 160IMPORTANT: In the SSL/TLS server, ASecureSocket::SSLSocket objects must be disconnected with SSL/TLS server's 161disconnect method to free used OpenSSL context and connection structures. Otherwise, you will have memory leaks. 162 163## Thread Safety 164 165Do not share ASocket or ASecureSocket objects across threads. 166 167## Installation 168 169You will need CMake to generate a makefile for the static library or to build the tests/code coverage program. 170 171Also make sure you have Google Test installed and OpenSSL updated to the lastest version. 172 173This tutorial will help you installing properly Google Test on Ubuntu: https://www.eriksmistad.no/getting-started-with-google-test-on-ubuntu/ 174 175If you work under Centos7 or another Linux distribution that has an old version of OpenSSL, this tutorial may help you to install the latest version of OpenSSL : https://blacksaildivision.com/how-to-install-openssl-on-centos in /usr/local/openssl. With the GUI version of CMake, click on "Advanced" and change these OpenSSL entries (or you can use the command line if you're comfortable with : 176 177OPENSSL_CRYPTO_LIBRARY : /usr/local/openssl/lib/libcrypto.so (.a will cause a link problem...) 178OPENSSL_INCLUDE_DIR : /usr/local/openssl/include 179OPENSSL_SSL_LIBRARY : /usr/local/openssl/lib/libssl.so (or .a if you want) 180 181The CMake script located in the tree will produce Makefiles for the creation of the static library and for the unit tests program. 182 183To create a debug static library and a test binary, change directory to the one containing the first CMakeLists.txt and : 184 185```Shell 186mkdir build 187cd build 188cmake .. -DCMAKE_BUILD_TYPE:STRING=Debug 189make 190``` 191 192To create a release static library, just change "Debug" by "Release". 193 194The library will be found under "build/[Debug|Release]/lib/libsocket.a" whereas the test program will be located in "build/[Debug|Release]/bin/test_socket" 195 196To directly run the unit test binary, you must indicate the path of the INI conf file (see the section below) 197```Shell 198./[Debug|Release]/bin/test_socket /path_to_your_ini_file/conf.ini 199``` 200 201### Building under Windows via Visual Studio 202 203First of all, you can download and install OpenSSL : https://slproweb.com/products/Win32OpenSSL.html (32 and 64 bits). 204 205Concerning Google Test, the library will be downloaded and built automatically from its github repository. 206 207Download and install the latest version of CMake : https://cmake.org/download/ (e.g. Windows win64-x64 Installer). 208 209Under C:\OpenSSL-Win64\bin\, you will find the DLLs : libcrypto-1_1-x64.dll and libssl-1_1-x64.dll that you will need, later, to place in the same directory as the binary file that used the socket-cpp library. 210 211Open CMake (cmake-gui). 212 213In "Where is the source code", put the socket-cpp path (e.g. C:/Users/Amine/Documents/Work/PROJECTS/GitHub/socket-cpp), where the main CMakeLists.txt file exist. 214 215In "Where to build binaries", paste the directory where you want to build the project (e.g. C:/Users/Amine/Documents/Work/PROJECTS/GitHub/socket-cpp/build). 216 217Click on "Configure". 218 219If you want to indicate a configuration file to run unit tests with "CTest", before clicking on "Configure", click on "Add entry" and add this entry : TEST_INI_FILE : C:/Users/Amine/Documents/Work/PROJECTS/GitHub/socket-cpp/my_test_conf.ini (tour configuration file). 220 221Then click on "Generate", you can choose a Visual Studio version if it is not done before (e.g. Visual Studio 15 2017 Win64) 222 223Finally, click on "Open Project" to open the solution in Visual Studio. 224 225In Visual Studio, you can change the build type (Debug -> Release). Build the solution (press F7). It must succeed without any errors. You can close Visual Studio. 226 227The library will be found under C:\Users\Amine\Documents\Work\PROJECTS\GitHub\socket-cpp\build\lib\Release\socket.lib 228 229After building a program using "socket.lib", do not forget to copy OpenSSL DLLs in the directory where the program binary is located. 230 231For example, in the build directory (e.g. C:\Users\Amine\Documents\Work\PROJECTS\GitHub\socket_build), under "bin", directory, you may find "Debug", "Release" or both according to the build type used during the build in Visual Studio, and in it, the test program "test_socket.exe". 232 233Before executing it, make sure to copy the OpenSSL DLLs in the same directory (e.g. copy C:\OpenSSL-Win64\bin\libcrypto-1_1-x64.dll and C:\OpenSSL-Win64\bin\libssl-1_1-x64.dll, do not change the name of the DLL !) 234 235If you have provided a valid configuration file to CMake-gui (TEST_INI_FILE), you can run unit tests in the command prompt (cmd.exe - after building the project in VS) by changing directory to the one where CMake generated the build files and by running : 236 237```Shell 238ctest -c "Release" 239``` 240 241to test the "Debug" library, just change "Release" by "Debug" (but ensure that you have build it before). Do not forget to place the DLLs near "test_socket.exe" before launching "ctest", otherwise, tests will fail. 242 243## Run Unit Tests 244 245[simpleini](https://github.com/brofield/simpleini) is used to gather unit tests parameters from 246an INI configuration file. You need to fill that file with some parameters. 247You can also disable some tests (SSL/TLS for instance) and indicate 248parameters only for the enabled tests. A template of the INI file already exists under SocketTest/ 249 250e.g. to enable SSL/TLS tests : 251 252```ini 253[tests] 254tcp-ssl=yes 255 256[tcp-ssl] 257server_port=4242 258ca_file=CAfile.pem 259ssl_cert_file=site.cert 260ssl_key_file=privkey.pem 261``` 262 263You can also generate an XML file of test results by adding --getst_output argument when calling the test program 264 265```Shell 266./[Debug|Release]/bin/test_socket /path_to_your_ini_file/conf.ini --gtest_output="xml:./TestSocket.xml" 267``` 268 269An alternative way to compile and run unit tests : 270 271```Shell 272mkdir build 273cd build 274cmake .. -DCMAKE_BUILD_TYPE=Debug -DTEST_INI_FILE="full_or_relative_path_to_your_test_conf.ini" 275make 276make test 277``` 278 279You may use a tool like https://github.com/adarmalik/gtest2html to convert your XML test result in an HTML file. 280 281## Memory Leak Check 282 283Visual Leak Detector has been used to check memory leaks with the Windows build (Visual Sutdio 2015) 284You can download it here: https://vld.codeplex.com/ 285 286To perform a leak check with the Linux build, you can do so : 287 288```Shell 289valgrind --leak-check=full ./Debug/bin/test_socket /path_to_ini_file/conf.ini 290``` 291 292## Code Coverage 293 294The code coverage build doesn't use the static library but compiles and uses directly the 295socket API in the test program. 296 297```Shell 298mkdir build 299cd build 300cmake .. -DCMAKE_BUILD_TYPE=Coverage -DCOVERAGE_INI_FILE:STRING="full_path_to_your_test_conf.ini" 301make 302make coverage_socket 303``` 304 305If everything is OK, the results will be found under ./SocketTest/coverage/index.html 306 307Make sure you feed CMake with a full path to your test conf INI file, otherwise, the coverage test 308will be useless. 309 310Under Visual Studio, you can simply use OpenCppCoverage (https://opencppcoverage.codeplex.com/) 311 312## CppCheck Compliancy 313 314The C++ code of the Socket C++ API classes is Cppcheck compliant. 315 316## Contribute 317All contributions are highly appreciated. This includes updating documentation, cleaning and writing code and unit tests to increase code coverage and enhance tools. 318 319Try to preserve the existing coding style (Hungarian notation, indentation etc...). 320