Skip to content

Commit 0eb26ad

Browse files
committed
Added Linux support and updated several files.
1 parent df2e2ef commit 0eb26ad

File tree

14 files changed

+775
-48
lines changed

14 files changed

+775
-48
lines changed

.gitignore

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
1+
CMakeCache.txt
2+
CMakeFiles/*
3+
Makefile
4+
SocketTest/CMakeCache.txt
5+
SocketTest/CMakeFiles/*
6+
SocketTest/Makefile
7+
SocketTest/bin/*
8+
SocketTest/cmake_install.cmake
9+
cmake_install.cmake
10+
lib/*
211

312
#Windows
413
.vs/*

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required(VERSION 2.6)
2+
3+
# Project configuration
4+
project(Socket)
5+
set(LIBRARY_OUTPUT_PATH lib/${CMAKE_BUILD_TYPE})
6+
set(CMAKE_CXX_STANDARD 14)
7+
8+
add_definitions(-DLINUX)
9+
10+
include_directories(Socket)
11+
12+
file(GLOB_RECURSE source_files Socket/*)
13+
add_library(socket STATIC ${source_files})

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Mohamed Amine Mzoughi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# TCP client/server API for C++
2+
[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)
3+
4+
5+
## About
6+
This is a simple TCP server/client for C++. Under Windows, it wraps WinSock and under Linux it wraps
7+
the related socket API (BSD compatible).
8+
It is meant to be a portable and easy-to-use API to create a TCP server or client.
9+
10+
Upcoming features : creating secure sockets with OpenSSL and using the sockets in an async way.
11+
12+
Compilation has been tested with:
13+
- GCC 5.4.0 (GNU/Linux Ubuntu 16.04 LTS)
14+
- Microsoft Visual Studio 2015 (Windows 10)
15+
16+
## Usage
17+
Create an object and provide to its constructor a callable object (for log printing) having this signature :
18+
19+
```cpp
20+
void(const std::string&)
21+
```
22+
23+
This section will be completed soon....
24+
25+
## Thread Safety
26+
27+
Do not share ASocket objects across threads.
28+
29+
## Installation
30+
You will need CMake to generate a makefile for the static library or to build the tests/code coverage
31+
program.
32+
33+
Also make sure you have libcurl and Google Test installed.
34+
35+
You can follow this script https://gist.github.com/fideloper/f72997d2e2c9fbe66459 to install libcurl.
36+
37+
This tutorial will help you installing properly Google Test on Ubuntu: https://www.eriksmistad.no/getting-started-with-google-test-on-ubuntu/
38+
39+
The CMake script located in the tree will produce a makefile for the creation of a static library,
40+
whereas the one under SocketTest will produce the unit tests program.
41+
42+
To create a debug static library, change directory to the one containing the first CMakeLists.txt
43+
44+
```Shell
45+
cmake . -DCMAKE_BUILD_TYPE:STRING=Debug
46+
make
47+
```
48+
49+
To create a release static library, just change "Debug" by "Release".
50+
51+
The library will be found under lib/[BUILD_TYPE]/libsocket.a
52+
53+
For the unit tests program, first build the static library and use the same build type when
54+
building it :
55+
56+
```Shell
57+
cd SocketTest
58+
cmake . -DCMAKE_BUILD_TYPE=Debug # or Release
59+
make
60+
```
61+
62+
To run it :
63+
```Shell
64+
./bin/[BUILD_TYPE]/test_socket
65+
```
66+
67+
## Run Unit Tests
68+
69+
You can generate an XML file of test results by adding this argument when calling the test program
70+
71+
```Shell
72+
./bin/[BUILD_TYPE]/test_socket --gtest_output="xml:./TestSocket.xml"
73+
```
74+
75+
## Memory Leak Check
76+
77+
Visual Leak Detector has been used to check memory leaks with the Windows build (Visual Sutdio 2015)
78+
You can download it here: https://vld.codeplex.com/
79+
80+
To perform a leak check with the Linux build, you can do so :
81+
82+
```Shell
83+
valgrind --leak-check=full ./bin/Debug/test_socket
84+
```
85+
86+
## Code Coverage
87+
88+
The code coverage build doesn't use the static library but compiles and uses directly the
89+
socket API in the test program.
90+
91+
by the location of your ini file and launch the code coverage :
92+
93+
```Shell
94+
cd SocketTest
95+
cmake . -DCMAKE_BUILD_TYPE=Coverage
96+
make
97+
make coverage_socket
98+
```
99+
100+
If everything is OK, the results will be found under ./SocketTest/coverage/index.html
101+
102+
Under Visual Studio, you can simply use OpenCppCoverage (https://opencppcoverage.codeplex.com/)
103+
104+
## CppCheck Compliancy
105+
106+
The C++ code of the Socket C++ API classes is Cppcheck compliant.
107+
108+
## Contribute
109+
All contributions are highly appreciated. This includes updating documentation, writing code and unit tests
110+
to increase code coverage and enhance tools.
111+
112+
Try to preserve the existing coding style (Hungarian notation, indentation etc...).

Socket/Socket.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
// Static members initialization
1010
volatile int ASocket::s_iSocketCount = 0;
1111
std::mutex ASocket::s_mtxCount;
12+
13+
#ifdef WINDOWS
1214
WSADATA ASocket::s_wsaData;
15+
#endif
1316

1417
/**
1518
* @brief constructor of the Socket

Socket/Socket.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <cstdio> // snprintf
1313
#include <exception>
1414
#include <functional>
15+
#include <memory>
1516
#include <mutex>
1617
#include <stdarg.h> // va_start, etc.
1718
#include <stdexcept>
@@ -22,6 +23,19 @@
2223

2324
// Need to link with Ws2_32.lib
2425
#pragma comment(lib,"WS2_32.lib")
26+
27+
#else
28+
#include <arpa/inet.h>
29+
#include <errno.h>
30+
#include <netinet/in.h>
31+
#include <netdb.h>
32+
#include <stdio.h>
33+
#include <stdlib.h>
34+
#include <string.h>
35+
#include <string.h>
36+
#include <sys/types.h>
37+
#include <sys/socket.h>
38+
#include <unistd.h>
2539
#endif
2640

2741
class ASocket
@@ -35,7 +49,8 @@ class ASocket
3549
#ifdef WINDOWS
3650
typedef SOCKET Socket;
3751
#else
38-
52+
typedef int Socket;
53+
#define INVALID_SOCKET -1
3954
#endif
4055

4156
enum SettingsFlag

Socket/TCPClient.cpp

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
CTCPClient::CTCPClient(LogFnCallback oLogger) :
1010
ASocket(oLogger),
1111
m_eStatus(DISCONNECTED),
12+
#ifdef WINDOWS
1213
m_ConnectSocket(INVALID_SOCKET),
1314
m_pResultAddrInfo(nullptr)
15+
#else
16+
m_pServer(nullptr)
17+
#endif
1418
//m_uRetryCount(0),
1519
//m_uRetryPeriod(0)
1620
{
@@ -26,6 +30,7 @@ bool CTCPClient::Connect(const std::string& strServer, const std::string& strPor
2630
m_oLog("[TCPClient][Warning] Opening a new connexion. The last one was automatically closed.");
2731
}
2832

33+
#ifdef WINDOWS
2934
ZeroMemory(&m_HintsAddrInfo, sizeof(m_HintsAddrInfo));
3035
/* AF_INET is used to specify the IPv4 address family. */
3136
m_HintsAddrInfo.ai_family = AF_INET;
@@ -50,9 +55,9 @@ bool CTCPClient::Connect(const std::string& strServer, const std::string& strPor
5055
}
5156

5257
// socket creation
53-
m_ConnectSocket = socket(m_pResultAddrInfo->ai_family,
54-
m_pResultAddrInfo->ai_socktype,
55-
m_pResultAddrInfo->ai_protocol);
58+
m_ConnectSocket = socket(m_pResultAddrInfo->ai_family, // AF_INET
59+
m_pResultAddrInfo->ai_socktype, // SOCK_STREAM
60+
m_pResultAddrInfo->ai_protocol);// IPPROTO_TCP
5661

5762
if (m_ConnectSocket == INVALID_SOCKET)
5863
{
@@ -113,36 +118,118 @@ bool CTCPClient::Connect(const std::string& strServer, const std::string& strPor
113118
}
114119
m_oLog(StringFormat("[TCPClient][Error] Unable to connect to server : %d", WSAGetLastError()));
115120

121+
#else
122+
// socket creation
123+
m_ConnectSocket = socket(AF_INET, SOCK_STREAM, 0);
124+
if (m_ConnectSocket < 0)
125+
{
126+
m_oLog(StringFormat("[TCPClient][Error] opening socket: %s", strerror(errno)));
127+
return false;
128+
}
129+
130+
int iPort = atoi(strPort.c_str());
131+
m_pServer = gethostbyname(strServer.c_str());
132+
if (m_pServer == nullptr)
133+
{
134+
m_oLog("[TCPClient][Error] no such host.");
135+
return false;
136+
}
137+
138+
bzero(reinterpret_cast<char *>(&m_ServAddr), sizeof(m_ServAddr));
139+
140+
// copy address
141+
bcopy(reinterpret_cast<char *>(m_pServer->h_addr), // src
142+
reinterpret_cast<char *>(&m_ServAddr.sin_addr.s_addr), // dst
143+
m_pServer->h_length); // len
144+
145+
m_ServAddr.sin_family = AF_INET;
146+
m_ServAddr.sin_port = htons(iPort);
147+
148+
149+
// connexion to the server
150+
int iResult = connect(m_ConnectSocket,
151+
reinterpret_cast<struct sockaddr *>(&m_ServAddr),
152+
sizeof(m_ServAddr));
153+
if (iResult >= 0)
154+
{
155+
m_eStatus = CONNECTED;
156+
return true;
157+
}
158+
m_oLog(StringFormat("[TCPClient][Error] connecting : %s", strerror(errno)));
159+
#endif
160+
116161
return false;
117162
}
118163

119-
bool CTCPClient::SendData(const char* pData, size_t uSize) const
164+
bool CTCPClient::Send(const char* pData, size_t uSize) const
120165
{
121166
if (m_eStatus != CONNECTED)
122167
{
123168
m_oLog("[TCPClient][Error] send failed : not connected to a server.");
124169
return false;
125170
}
126171

127-
int iResult = send(m_ConnectSocket, pData, uSize, 0);
172+
int iResult = 0;
173+
#ifdef WINDOWS
174+
iResult = send(m_ConnectSocket, pData, uSize, 0);
128175
if (iResult == SOCKET_ERROR)
129176
{
130177
m_oLog(StringFormat("[TCPClient][Error] send failed : %d", WSAGetLastError()));
131178
//Disconnect();
132179
return false;
133180
}
181+
#else
182+
iResult = write(m_ConnectSocket, pData, uSize);
183+
if (iResult < 0)
184+
{
185+
m_oLog(StringFormat("[TCPClient][Error] writing to socket : %s", strerror(errno)));
186+
return false;
187+
}
188+
#endif
134189

135190
return true;
136191
}
137192

138-
bool CTCPClient::SendData(const std::string& strData) const
193+
bool CTCPClient::Send(const std::string& strData) const
139194
{
140-
return SendData(strData.c_str(), strData.length());
195+
return Send(strData.c_str(), strData.length());
141196
}
142197

143-
bool CTCPClient::SendData(const std::vector<char>& Data) const
198+
bool CTCPClient::Send(const std::vector<char>& Data) const
144199
{
145-
return SendData(Data.data(), Data.size());
200+
return Send(Data.data(), Data.size());
201+
}
202+
203+
/* ret > 0 : bytes received
204+
* ret == 0 : connection closed
205+
* ret < 0 : recv failed
206+
*/
207+
int CTCPClient::Receive(char* pData, size_t uSize) const
208+
{
209+
if (m_eStatus != CONNECTED)
210+
{
211+
m_oLog("[TCPClient][Error] recv failed : not connected to a server.");
212+
return -1;
213+
}
214+
215+
int iBytesRcvd = 0;
216+
217+
#ifdef WINDOWS
218+
iBytesRcvd = recv(m_ConnectSocket, pData, uSize, 0);
219+
if (iBytesRcvd < 0)
220+
{
221+
m_oLog(StringFormat("[TCPClient][Error] recv failed : %d", WSAGetLastError()));
222+
}
223+
#else
224+
//bzero(pData, uSize);
225+
iBytesRcvd = read(m_ConnectSocket, pData, uSize);
226+
if (iBytesRcvd < 0)
227+
{
228+
m_oLog(StringFormat("[TCPClient][Error] reading from socket : %s", strerror(errno)));
229+
}
230+
#endif
231+
232+
return iBytesRcvd;
146233
}
147234

148235
bool CTCPClient::Disconnect()
@@ -152,6 +239,7 @@ bool CTCPClient::Disconnect()
152239

153240
m_eStatus = DISCONNECTED;
154241

242+
#ifdef WINDOWS
155243
// shutdown the connection since no more data will be sent
156244
int iResult = shutdown(m_ConnectSocket, SD_SEND);
157245
if (iResult == SOCKET_ERROR)
@@ -161,13 +249,16 @@ bool CTCPClient::Disconnect()
161249
}
162250
closesocket(m_ConnectSocket);
163251

164-
m_ConnectSocket = INVALID_SOCKET;
165-
166252
if (m_pResultAddrInfo != nullptr)
167253
{
168254
freeaddrinfo(m_pResultAddrInfo);
169255
m_pResultAddrInfo = nullptr;
170256
}
257+
#else
258+
close(m_ConnectSocket);
259+
#endif
260+
261+
m_ConnectSocket = INVALID_SOCKET;
171262

172263
return true;
173264
}

0 commit comments

Comments
 (0)