1 /**
2 * @file Socket.cpp
3 * @brief implementation of the Socket class
4 * @author Mohamed Amine Mzoughi <mohamed-amine.mzoughi@laposte.net>
5 */
6
7 #include "Socket.h"
8
9 #include <iostream>
10 #include <vector>
11
12 #ifdef WINDOWS
13 WSADATA ASocket::s_wsaData;
14 #endif
15
instance()16 ASocket::SocketGlobalInitializer& ASocket::SocketGlobalInitializer::instance()
17 {
18 static SocketGlobalInitializer inst{};
19 return inst;
20 }
21
SocketGlobalInitializer()22 ASocket::SocketGlobalInitializer::SocketGlobalInitializer()
23 {
24 // In windows, this will init the winsock DLL stuff
25 #ifdef WINDOWS
26 // MAKEWORD(2,2) version 2.2 of Winsock
27 int iWinSockInitResult = WSAStartup(MAKEWORD(2, 2), &s_wsaData);
28
29 if (iWinSockInitResult != 0)
30 {
31 std::cerr << ASocket::StringFormat("[TCPClient][Error] WSAStartup failed : %d", iWinSockInitResult);
32 }
33 #endif
34 }
35
~SocketGlobalInitializer()36 ASocket::SocketGlobalInitializer::~SocketGlobalInitializer()
37 {
38 #ifdef WINDOWS
39 /* call WSACleanup when done using the Winsock dll */
40 WSACleanup();
41 #endif
42 }
43
44 /**
45 * @brief constructor of the Socket
46 *
47 * @param Logger - a callabck to a logger function void(const std::string&)
48 *
49 */
ASocket(const LogFnCallback & oLogger,const SettingsFlag eSettings)50 ASocket::ASocket(const LogFnCallback& oLogger,
51 const SettingsFlag eSettings /*= ALL_FLAGS*/) :
52 m_oLog(oLogger),
53 m_eSettingsFlags(eSettings),
54 m_globalInitializer(SocketGlobalInitializer::instance())
55 {
56
57 }
58
59 /**
60 * @brief destructor of the socket object
61 * It's a pure virtual destructor but an implementation is provided below.
62 * this to avoid creating a dummy pure virtual method to transform the class
63 * to an abstract one.
64 */
~ASocket()65 ASocket::~ASocket()
66 {
67
68 }
69
70 /**
71 * @brief returns a formatted string
72 *
73 * @param [in] strFormat string with one or many format specifiers
74 * @param [in] parameters to be placed in the format specifiers of strFormat
75 *
76 * @retval string formatted string
77 */
StringFormat(const std::string strFormat,...)78 std::string ASocket::StringFormat(const std::string strFormat, ...)
79 {
80 va_list args;
81 va_start (args, strFormat);
82 size_t len = std::vsnprintf(NULL, 0, strFormat.c_str(), args);
83 va_end (args);
84 std::vector<char> vec(len + 1);
85 va_start (args, strFormat);
86 std::vsnprintf(&vec[0], len + 1, strFormat.c_str(), args);
87 va_end (args);
88 return &vec[0];
89 }
90
91 /**
92 * @brief waits for a socket's read status change
93 *
94 * @param [in] sd socket descriptor to be selected
95 * @param [in] msec waiting period in milliseconds, a value of 0 implies no timeout
96 *
97 * @retval int 0 on timeout, -1 on error and 1 on success.
98 */
SelectSocket(const ASocket::Socket sd,const size_t msec)99 int ASocket::SelectSocket(const ASocket::Socket sd, const size_t msec)
100 {
101 if (sd < 0)
102 {
103 return -1;
104 }
105
106 struct timeval tval;
107 struct timeval* tvalptr = nullptr;
108 fd_set rset;
109 int res;
110
111 if (msec > 0)
112 {
113 tval.tv_sec = msec / 1000;
114 tval.tv_usec = (msec % 1000) * 1000;
115 tvalptr = &tval;
116 }
117
118 FD_ZERO(&rset);
119 FD_SET(sd, &rset);
120
121 // block until socket is readable.
122 res = select(sd + 1, &rset, nullptr, nullptr, tvalptr);
123
124 if (res <= 0)
125 return res;
126
127 if (!FD_ISSET(sd, &rset))
128 return -1;
129
130 return 1;
131 }
132
133 /**
134 * @brief waits for a set of sockets read status change
135 *
136 * @param [in] pSocketsToSelect pointer to an array of socket descriptors to be selected
137 * @param [in] count elements count of pSocketsToSelect
138 * @param [in] msec waiting period in milliseconds, a value of 0 implies no timeout
139 * @param [out] selectedIndex index of the socket that is ready to be read
140 *
141 * @retval int 0 on timeout, -1 on error and 1 on success.
142 */
SelectSockets(const ASocket::Socket * pSocketsToSelect,const size_t count,const size_t msec,size_t & selectedIndex)143 int ASocket::SelectSockets(const ASocket::Socket* pSocketsToSelect, const size_t count,
144 const size_t msec, size_t& selectedIndex)
145 {
146 if (!pSocketsToSelect || count == 0)
147 {
148 return -1;
149 }
150
151 fd_set rset;
152 int res = -1;
153
154 struct timeval tval;
155 struct timeval* tvalptr = nullptr;
156 if (msec > 0)
157 {
158 tval.tv_sec = msec / 1000;
159 tval.tv_usec = (msec % 1000) * 1000;
160 tvalptr = &tval;
161 }
162
163 FD_ZERO(&rset);
164
165 int max_fd = -1;
166 for (size_t i = 0; i < count; i++)
167 {
168 FD_SET(pSocketsToSelect[i], &rset);
169
170 if (pSocketsToSelect[i] > max_fd)
171 {
172 max_fd = pSocketsToSelect[i];
173 }
174 }
175
176 // block until one socket is ready to read.
177 res = select(max_fd + 1, &rset, nullptr, nullptr, tvalptr);
178
179 if (res <= 0)
180 return res;
181
182 // find the first socket which has some activity.
183 for (size_t i = 0; i < count; i++)
184 {
185 if (FD_ISSET(pSocketsToSelect[i], &rset))
186 {
187 selectedIndex = i;
188 return 1;
189 }
190 }
191
192 return -1;
193 }
194
195 /**
196 * @brief converts a value representing milliseconds into a struct timeval
197 *
198 * @param [time_msec] a time value in milliseconds
199 *
200 * @retval time_msec converted to struct timeval
201 */
TimevalFromMsec(unsigned int time_msec)202 struct timeval ASocket::TimevalFromMsec(unsigned int time_msec){
203 struct timeval t;
204
205 t.tv_sec = time_msec / 1000;
206 t.tv_usec = (time_msec % 1000) * 1000;
207
208 return t;
209 }
210