19 #define _WINSOCK_DEPRECATED_NO_WARNINGS 23 #pragma comment(lib, "WS2_32.LIB") 27 #define INVALID_SOCKET (-1) 28 #include <arpa/inet.h> 31 #include <netinet/in.h> 32 #include <netinet/tcp.h> 33 #include <sys/ioctl.h> 34 #include <sys/socket.h> 35 #include <sys/types.h> 54 WORD wVersionRequested;
57 wVersionRequested = MAKEWORD(2, 0);
59 if (WSAStartup(wVersionRequested, &wsaData))
62 m_hSock = INVALID_SOCKET;
76 catch (
const std::exception& e)
78 std::cerr <<
"[~CClientTCPSocket] Exception:\n" 94 if (m_hSock != INVALID_SOCKET)
98 m_hSock = INVALID_SOCKET;
104 shutdown(m_hSock, SHUT_RDWR);
119 return readAsync(Buffer, Count);
131 return writeAsync(Buffer, Count);
138 Write(str.c_str(), str.size());
145 const std::string& remotePartAddress,
unsigned short remotePartTCPPort,
146 unsigned int timeout_ms)
151 if (m_hSock != INVALID_SOCKET) close();
154 if (INVALID_SOCKET == (m_hSock = socket(AF_INET, SOCK_STREAM, 0)))
156 "Error creating new client socket:\n%s",
157 getLastErrorStr().c_str()));
159 struct sockaddr_in otherAddress;
161 otherAddress.sin_family = AF_INET;
162 otherAddress.sin_port = htons(remotePartTCPPort);
165 std::string solved_IP;
167 remotePartAddress, solved_IP, DNS_LOOKUP_TIMEOUT_MS))
169 "DNS lookup failed for '%s'", remotePartAddress.c_str());
172 otherAddress.sin_addr.s_addr = inet_addr(solved_IP.c_str());
173 if (INADDR_NONE == otherAddress.sin_addr.s_addr)
175 "Invalid IP address provided: %s", solved_IP.c_str());
179 unsigned long non_block_mode = 1;
180 if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
181 THROW_EXCEPTION(
"Error entering non-blocking mode with ioctlsocket();");
183 int oldflags = fcntl(m_hSock, F_GETFL, 0);
184 if (oldflags == -1)
THROW_EXCEPTION(
"Error retrieving fcntl();of socket.");
185 oldflags |= O_NONBLOCK;
186 if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
192 m_hSock, (
struct sockaddr*)&otherAddress,
sizeof(otherAddress));
194 int er = WSAGetLastError();
195 if (r < 0 && er != WSAEINPROGRESS && er != WSAEWOULDBLOCK)
198 if (r < 0 && er != EINPROGRESS)
201 "Error connecting to %s:%hu. Error: %s [%d]",
202 remotePartAddress.c_str(), remotePartTCPPort, strerror(er), er));
205 timeval timer = {0, 0};
206 fd_set ss_write, ss_errors;
209 FD_SET(m_hSock, &ss_write);
210 FD_SET(m_hSock, &ss_errors);
212 timer.tv_sec = timeout_ms / 1000;
213 timer.tv_usec = 1000 * (timeout_ms % 1000);
215 int sel_ret = select(
220 timeout_ms == 0 ?
nullptr : &timer);
224 "Timeout connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
225 remotePartTCPPort, getLastErrorStr().c_str()));
228 "Error connecting to '%s:%hu':\n%s", remotePartAddress.c_str(),
229 remotePartTCPPort, getLastErrorStr().c_str()));
234 int lon =
sizeof(int);
235 getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (
char*)(&valopt), &
lon);
238 getsockopt(m_hSock, SOL_SOCKET, SO_ERROR, (
void*)(&valopt), &
lon);
244 "Error connecting to %s:%hu. Error: %i.", remotePartAddress.c_str(),
245 remotePartTCPPort, valopt));
249 "Error connecting to %s:%hu. Error: %s.", remotePartAddress.c_str(),
250 remotePartTCPPort, strerror(valopt)));
257 if (ioctlsocket(m_hSock, FIONBIO, &non_block_mode))
260 oldflags &= ~O_NONBLOCK;
261 if (-1 == fcntl(m_hSock, F_SETFL, oldflags))
266 m_remotePartIP = remotePartAddress;
279 void* Buffer,
const size_t Count,
const int timeoutStart_ms,
280 const int timeoutBetween_ms)
284 if (m_hSock == INVALID_SOCKET)
return 0;
286 size_t remainToRead, alreadyRead = 0;
288 bool timeoutExpired =
false;
290 struct timeval timeoutSelect = {0, 0};
291 struct timeval* ptrTimeout;
296 FD_SET(m_hSock, &sockArr);
299 while (alreadyRead < Count && !timeoutExpired)
302 int curTimeout = alreadyRead == 0 ? timeoutStart_ms : timeoutBetween_ms;
305 ptrTimeout =
nullptr;
308 timeoutSelect.tv_sec = curTimeout / 1000;
309 timeoutSelect.tv_usec = 1000 * (curTimeout % 1000);
310 ptrTimeout = &timeoutSelect;
314 int selRet = ::select(
321 if (selRet == INVALID_SOCKET)
323 "Error reading from socket: %s", getLastErrorStr().c_str());
328 timeoutExpired =
true;
333 remainToRead = Count - alreadyRead;
337 m_hSock, ((
char*)Buffer) + alreadyRead, (
int)remainToRead, 0);
339 if (readNow != INVALID_SOCKET)
342 alreadyRead += readNow;
351 if (readNow == 0 && remainToRead != 0)
355 timeoutExpired =
true;
370 const void* Buffer,
const size_t Count,
const int timeout_ms)
374 if (m_hSock == INVALID_SOCKET)
return 0;
376 size_t remainToWrite, alreadyWritten = 0;
378 bool timeoutExpired =
false;
380 struct timeval timeoutSelect = {0, 0};
381 struct timeval* ptrTimeout;
386 FD_SET(m_hSock, &sockArr);
391 ptrTimeout =
nullptr;
395 timeoutSelect.tv_sec = timeout_ms / 1000;
396 timeoutSelect.tv_usec = 1000 * (timeout_ms % 1000);
397 ptrTimeout = &timeoutSelect;
401 while (alreadyWritten < Count && !timeoutExpired)
404 int selRet = ::select(
411 if (selRet == INVALID_SOCKET)
413 "Error writing to socket: %s", getLastErrorStr().c_str());
418 timeoutExpired =
true;
425 remainToWrite = Count - alreadyWritten;
429 m_hSock, ((
char*)Buffer) + alreadyWritten, (
int)remainToWrite,
432 if (writtenNow != INVALID_SOCKET)
435 alreadyWritten += writtenNow;
441 return alreadyWritten;
451 if (m_hSock == INVALID_SOCKET)
return 0;
452 unsigned long ret = 0;
455 ioctlsocket(m_hSock, FIONREAD, &ret)
457 ioctl(m_hSock, FIONREAD, &ret)
472 int length =
sizeof(newValue);
475 m_hSock, IPPROTO_TCP, TCP_NODELAY, (
char*)&newValue, length);
485 int length =
sizeof(value);
487 unsigned int length =
sizeof(value);
490 getsockopt(m_hSock, IPPROTO_TCP, TCP_NODELAY, (
char*)&value, &length);
503 const unsigned int length =
sizeof(newValue);
505 return setsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (
char*)&newValue, length);
515 int length =
sizeof(value);
517 unsigned int length =
sizeof(value);
519 getsockopt(m_hSock, SOL_SOCKET, SO_SNDBUF, (
char*)&value, &length);
530 [[maybe_unused]] int64_t off, [[maybe_unused]] CStream::TSeekOrigin org)
static unsigned int DNS_LOOKUP_TIMEOUT_MS
See description of CClientTCPSocket.
int setSOSendBufffer(int newValue)
Set the size of the SO send buffer.
void connect(const std::string &remotePartAddress, unsigned short remotePartTCPPort, unsigned int timeout_ms=0)
Establishes a connection with a remote part.
size_t Write(const void *Buffer, size_t Count) override
Introduces a virtual method responsible for writing to the stream.
#define THROW_EXCEPTION(msg)
std::string std::string format(std::string_view fmt, ARGS &&... args)
bool isConnected()
Returns true if this objects represents a successfully connected socket.
uint64_t Seek(int64_t off, CStream::TSeekOrigin org=sFromBeginning) override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
std::string getLastSocketErrorStr()
Returns a description of the last Sockets error.
int getSOSendBufffer()
Return the current size of the SO send buffer.
size_t Read(void *Buffer, size_t Count) override
Introduces a virtual method responsible for reading from the stream (This method BLOCKS) This method ...
uint64_t getTotalBytesCount() const override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
~CClientTCPSocket() override
Destructor.
bool DNS_resolve_async(const std::string &server_name, std::string &out_ip, const unsigned int timeout_ms=3000)
Resolve a server address by its name, returning its IP address as a string - This method has a timeou...
CClientTCPSocket()
Default constructor.
int getTCPNoDelay()
Return the value of the TCPNoDelay option.
std::string getLastErrorStr()
Returns a description of the last Sockets error.
size_t writeAsync(const void *Buffer, const size_t Count, const int timeout_ms=-1)
A method for writing to the socket with optional timeouts.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
int setTCPNoDelay(int newValue)
Set the TCP no delay option of the protocol (Nagle algorithm).
uint64_t getPosition() const override
This virtual method has no effect in this implementation over a TCP socket, and its use raises an exc...
void close()
Closes the connection.
std::string exception_to_str(const std::exception &e)
Builds a nice textual representation of a nested exception, which if generated using MRPT macros (THR...
size_t getReadPendingBytes()
Return the number of bytes already in the receive queue (they can be read without waiting) ...
Serial and networking devices and utilities.
size_t readAsync(void *Buffer, const size_t Count, const int timeoutStart_ms=-1, const int timeoutBetween_ms=-1)
A method for reading from the socket with an optional timeout.
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
void sendString(const std::string &str)
Writes a string to the socket.