172 lines
3.9 KiB
C++
172 lines
3.9 KiB
C++
#include "IConnection.h"
|
|
#include "IConnectionClient.h"
|
|
#include "IConnection.inl"
|
|
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <fcntl.h>
|
|
#include <cerrno>
|
|
|
|
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
#elif defined TARGET_OS_Windows
|
|
#include <cerrno>
|
|
|
|
#include <WS2tcpip.h>
|
|
#else
|
|
#endif
|
|
|
|
namespace Socket {
|
|
class CConnectionClient final : public TConnection<IConnectionClient>
|
|
{
|
|
public:
|
|
|
|
bool connect(const char* sServerName, const size_t serverPort, const size_t timeOut) override
|
|
{
|
|
if (!open()) { return false; }
|
|
|
|
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
|
long value;
|
|
// Sets non blocking
|
|
if((value = ::fcntl(m_socket, F_GETFL, nullptr)) < 0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
value|=O_NONBLOCK;
|
|
if(::fcntl(m_socket, F_SETFL, value)<0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
// Looks up host name
|
|
struct hostent* serverHostEntry = gethostbyname(sServerName);
|
|
if(!serverHostEntry)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
#elif defined TARGET_OS_Windows
|
|
|
|
// Sets non blocking
|
|
unsigned long mode = 1;
|
|
ioctlsocket(m_socket, FIONBIO, &mode);
|
|
|
|
struct addrinfo hints;
|
|
ZeroMemory(&hints, sizeof(hints));
|
|
hints.ai_family = AF_INET;
|
|
PADDRINFOA addr;
|
|
if (getaddrinfo(sServerName, nullptr, &hints, &addr) != 0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
const auto sockaddrIPV4 = reinterpret_cast<sockaddr_in*>(addr->ai_addr);
|
|
|
|
#endif
|
|
|
|
// Connects
|
|
struct sockaddr_in serverAddress;
|
|
memset(&serverAddress, 0, sizeof(serverAddress));
|
|
serverAddress.sin_family = AF_INET;
|
|
serverAddress.sin_port = htons(static_cast<unsigned short>(serverPort));
|
|
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
|
serverAddress.sin_addr=*((struct in_addr*)serverHostEntry->h_addr);
|
|
#elif defined TARGET_OS_Windows
|
|
serverAddress.sin_addr = sockaddrIPV4->sin_addr;
|
|
freeaddrinfo(addr);
|
|
#endif
|
|
errno = 0;
|
|
if (::connect(m_socket, reinterpret_cast<struct sockaddr*>(&serverAddress), sizeof(struct sockaddr_in)) < 0)
|
|
{
|
|
bool inProgress;
|
|
|
|
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
|
inProgress = (errno==EINPROGRESS);
|
|
#elif defined TARGET_OS_Windows
|
|
inProgress = (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK);
|
|
#else
|
|
inProgress = false;
|
|
#endif
|
|
|
|
if (inProgress)
|
|
{
|
|
const int t = (timeOut == 0xffffffff) ? 125 : int(timeOut);
|
|
// Performs time out
|
|
|
|
struct timeval timeVal;
|
|
timeVal.tv_sec = (t / 1000);
|
|
timeVal.tv_usec = ((t - timeVal.tv_sec * 1000) * 1000);
|
|
|
|
fd_set writeFileDesc;
|
|
FD_ZERO(&writeFileDesc);
|
|
FD_SET(m_socket, &writeFileDesc);
|
|
|
|
if (select(int(m_socket + 1), nullptr, &writeFileDesc, nullptr, &timeVal) < 0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
if (!FD_ISSET_PROXY(int(m_socket), &writeFileDesc))
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
// Checks error status
|
|
int option = 0;
|
|
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
|
socklen_t length = sizeof(option);
|
|
::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, (void*)(&option), &length);
|
|
#elif defined TARGET_OS_Windows
|
|
int length = sizeof(option);
|
|
getsockopt(m_socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&option), &length);
|
|
#endif
|
|
if (option != 0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
|
|
|
|
// Sets back to blocking
|
|
if((value=::fcntl(m_socket, F_GETFL, nullptr))<0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
value&=~O_NONBLOCK;
|
|
if(::fcntl(m_socket, F_SETFL, value)<0)
|
|
{
|
|
close();
|
|
return false;
|
|
}
|
|
|
|
#elif defined TARGET_OS_Windows
|
|
|
|
// Sets back to blocking
|
|
mode = 0;
|
|
ioctlsocket(m_socket, FIONBIO, &mode);
|
|
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
IConnectionClient* createConnectionClient() { return new CConnectionClient(); }
|
|
} // namespace Socket
|