/** * @file Socket.cpp * @brief implementation of the Socket class * @author Mohamed Amine Mzoughi */ #include "Socket.h" #include #include #ifdef WINDOWS WSADATA ASocket::s_wsaData; #endif ASocket::SocketGlobalInitializer& ASocket::SocketGlobalInitializer::instance() { static SocketGlobalInitializer inst{}; return inst; } ASocket::SocketGlobalInitializer::SocketGlobalInitializer() { // In windows, this will init the winsock DLL stuff #ifdef WINDOWS // MAKEWORD(2,2) version 2.2 of Winsock int iWinSockInitResult = WSAStartup(MAKEWORD(2, 2), &s_wsaData); if (iWinSockInitResult != 0) { std::cerr << ASocket::StringFormat("[TCPClient][Error] WSAStartup failed : %d", iWinSockInitResult); } #endif } ASocket::SocketGlobalInitializer::~SocketGlobalInitializer() { #ifdef WINDOWS /* call WSACleanup when done using the Winsock dll */ WSACleanup(); #endif } /** * @brief constructor of the Socket * * @param Logger - a callabck to a logger function void(const std::string&) * */ ASocket::ASocket(const LogFnCallback& oLogger, const SettingsFlag eSettings /*= ALL_FLAGS*/) : m_oLog(oLogger), m_eSettingsFlags(eSettings), m_globalInitializer(SocketGlobalInitializer::instance()) { } /** * @brief destructor of the socket object * It's a pure virtual destructor but an implementation is provided below. * this to avoid creating a dummy pure virtual method to transform the class * to an abstract one. */ ASocket::~ASocket() { } /** * @brief returns a formatted string * * @param [in] strFormat string with one or many format specifiers * @param [in] parameters to be placed in the format specifiers of strFormat * * @retval string formatted string */ std::string ASocket::StringFormat(const std::string strFormat, ...) { va_list args; va_start (args, strFormat); size_t len = std::vsnprintf(NULL, 0, strFormat.c_str(), args); va_end (args); std::vector vec(len + 1); va_start (args, strFormat); std::vsnprintf(&vec[0], len + 1, strFormat.c_str(), args); va_end (args); return &vec[0]; } /** * @brief waits for a socket's read status change * * @param [in] sd socket descriptor to be selected * @param [in] msec waiting period in milliseconds, a value of 0 implies no timeout * * @retval int 0 on timeout, -1 on error and 1 on success. */ int ASocket::SelectSocket(const ASocket::Socket sd, const size_t msec) { if (sd < 0) { return -1; } struct timeval tval; struct timeval* tvalptr = nullptr; fd_set rset; int res; if (msec > 0) { tval.tv_sec = msec / 1000; tval.tv_usec = (msec % 1000) * 1000; tvalptr = &tval; } FD_ZERO(&rset); FD_SET(sd, &rset); // block until socket is readable. res = select(sd + 1, &rset, nullptr, nullptr, tvalptr); if (res <= 0) return res; if (!FD_ISSET(sd, &rset)) return -1; return 1; } /** * @brief waits for a set of sockets read status change * * @param [in] pSocketsToSelect pointer to an array of socket descriptors to be selected * @param [in] count elements count of pSocketsToSelect * @param [in] msec waiting period in milliseconds, a value of 0 implies no timeout * @param [out] selectedIndex index of the socket that is ready to be read * * @retval int 0 on timeout, -1 on error and 1 on success. */ int ASocket::SelectSockets(const ASocket::Socket* pSocketsToSelect, const size_t count, const size_t msec, size_t& selectedIndex) { if (!pSocketsToSelect || count == 0) { return -1; } fd_set rset; int res = -1; struct timeval tval; struct timeval* tvalptr = nullptr; if (msec > 0) { tval.tv_sec = msec / 1000; tval.tv_usec = (msec % 1000) * 1000; tvalptr = &tval; } FD_ZERO(&rset); int max_fd = -1; for (size_t i = 0; i < count; i++) { FD_SET(pSocketsToSelect[i], &rset); if (pSocketsToSelect[i] > max_fd) { max_fd = pSocketsToSelect[i]; } } // block until one socket is ready to read. res = select(max_fd + 1, &rset, nullptr, nullptr, tvalptr); if (res <= 0) return res; // find the first socket which has some activity. for (size_t i = 0; i < count; i++) { if (FD_ISSET(pSocketsToSelect[i], &rset)) { selectedIndex = i; return 1; } } return -1; } /** * @brief converts a value representing milliseconds into a struct timeval * * @param [time_msec] a time value in milliseconds * * @retval time_msec converted to struct timeval */ struct timeval ASocket::TimevalFromMsec(unsigned int time_msec){ struct timeval t; t.tv_sec = time_msec / 1000; t.tv_usec = (time_msec % 1000) * 1000; return t; }