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