1# TCP client/server API for C++ (with SSL/TLS support)
2[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](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