TcpSocket.cpp
Go to the documentation of this file.
1 //
2 // Copyright (c) 2023 SICK AG, Waldkirch
3 //
4 // SPDX-License-Identifier: Unlicense
5 
6 #include "TcpSocket.h"
7 
8 #include <fcntl.h>
9 
10 #include <iostream>
11 #include <stdexcept>
12 
13 #include "NumericConv.h"
14 #include "SockRecord.h"
15 
16 namespace visionary {
17 
18 #ifdef _WIN32
19 using bufsize_t = int;
20 #else
21 using bufsize_t = size_t;
22 #endif
23 
24 TcpSocket::TcpSocket() : m_pSockRecord(new SockRecord())
25 {
26 }
27 
29 {
30  if (m_pSockRecord->isValid())
31  {
32  shutdown();
33  }
34 }
35 
36 int TcpSocket::connect(const std::string& ipaddr, std::uint16_t port, std::uint32_t timeoutMs)
37 {
38  int iResult = 0;
39 
40  if (m_pSockRecord->isValid())
41  {
42  shutdown();
43  }
44 
45 #ifdef _WIN32
46  //-----------------------------------------------
47  // Initialize Winsock
48  WSADATA wsaData;
49  iResult = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
50  if (iResult != NO_ERROR)
51  {
52  return iResult;
53  }
54 #endif
55 
56  //-----------------------------------------------
57  // Create a receiver socket to receive datagrams
58  SOCKET hsock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
59 
60  if (hsock == INVALID_SOCKET)
61  {
62  m_pSockRecord->invalidate();
63  return -1;
64  }
65 
66  m_pSockRecord->set(hsock);
67 
68  //-----------------------------------------------
69  // Bind the socket to any address and the specified port.
70  sockaddr_in recvAddr{};
71  recvAddr.sin_family = AF_INET;
72  recvAddr.sin_port = htons(port);
73  if (::inet_pton(AF_INET, ipaddr.c_str(), &recvAddr.sin_addr.s_addr) <= 0)
74  {
75  // invalid address or buffer size
76  return -1;
77  }
78  {
79 #ifdef _WIN32
80  unsigned long block = 1;
81  ::ioctlsocket(static_cast<unsigned int>(m_pSockRecord->socket()), static_cast<int>(FIONBIO), &block);
82 #else
83  int flags = fcntl(m_pSockRecord->socket(), F_GETFL, 0);
84  if (flags == -1)
85  {
86  close(m_pSockRecord->socket());
87 
88  m_pSockRecord->invalidate();
89  return -1;
90  }
91  flags |= O_NONBLOCK;
92  if (::fcntl(m_pSockRecord->socket(), F_SETFL, flags) == -1)
93  {
94  close(m_pSockRecord->socket());
95  m_pSockRecord->invalidate();
96  return -1;
97  }
98 #endif
99  }
100  // calculate the timeout in seconds and microseconds
101 #ifdef _WIN32
102  long timeoutSeconds = static_cast<long>(timeoutMs / 1000U);
103  long timeoutUSeconds = static_cast<long>((timeoutMs % 1000U) * 1000U);
104 #else
105  time_t timeoutSeconds = static_cast<time_t>(timeoutMs / 1000U);
106  suseconds_t timeoutUSeconds = static_cast<time_t>((timeoutMs % 1000U) * 1000U);
107 #endif
108  // applying the calculated values
109  struct timeval tv;
110  tv.tv_sec = timeoutSeconds;
111  tv.tv_usec = timeoutUSeconds;
112 
113  iResult = ::connect(m_pSockRecord->socket(), reinterpret_cast<sockaddr*>(&recvAddr), sizeof(recvAddr));
114  if (iResult != 0)
115  {
116 #ifdef _WIN32
117  if (::WSAGetLastError() != WSAEWOULDBLOCK)
118  {
119  ::closesocket(m_pSockRecord->socket());
120  m_pSockRecord->invalidate();
121  return -1;
122  }
123 #else
124  if (errno != EINPROGRESS)
125  {
126  close(m_pSockRecord->socket());
127  m_pSockRecord->invalidate();
128  return -1;
129  }
130 #endif
131  fd_set setW, setE;
132  FD_ZERO(&setW);
133  FD_SET(m_pSockRecord->socket(), &setW);
134  FD_ZERO(&setE);
135  FD_SET(m_pSockRecord->socket(), &setE);
136  int ret = ::select(static_cast<int>(m_pSockRecord->socket() + 1), nullptr, &setW, &setE, &tv);
137 #ifdef _WIN32
138  if (ret <= 0)
139  {
140  // select() failed or connection timed out
141  ::closesocket(m_pSockRecord->socket());
142  m_pSockRecord->invalidate();
143  if (ret == 0)
144  {
145  ::WSASetLastError(WSAETIMEDOUT);
146  }
147  return -1;
148  }
149  if (FD_ISSET(m_pSockRecord->socket(), &setE))
150  {
151  // connection failed
152  int error_code;
153  int error_code_size = sizeof(error_code);
154  ::getsockopt(
155  m_pSockRecord->socket(), SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error_code), &error_code_size);
156  ::closesocket(m_pSockRecord->socket());
157  m_pSockRecord->invalidate();
158  ::WSASetLastError(error_code);
159  return -1;
160  }
161 #else
162  {
163  int so_error;
164  socklen_t len = sizeof(so_error);
165  ::getsockopt(m_pSockRecord->socket(), SOL_SOCKET, SO_ERROR, &ret, &len);
166  if (ret != 0)
167  {
168  ::close(m_pSockRecord->socket());
169  m_pSockRecord->invalidate();
170  return ret;
171  }
172  }
173 #endif
174  }
175 
176  {
177 #ifdef _WIN32
178  unsigned long block = 0;
179  if (::ioctlsocket(m_pSockRecord->socket(), static_cast<int>(FIONBIO), &block) == SOCKET_ERROR)
180  {
181  ::closesocket(m_pSockRecord->socket());
182  m_pSockRecord->invalidate();
183  return -1;
184  }
185 #else
186  {
187  int flags = ::fcntl(m_pSockRecord->socket(), F_GETFL, 0);
188  if (flags == -1)
189  {
190  ::close(m_pSockRecord->socket());
191  m_pSockRecord->invalidate();
192  return -1;
193  }
194  flags &= ~O_NONBLOCK;
195  if (::fcntl(m_pSockRecord->socket(), F_SETFL, flags) == -1)
196  {
197  ::close(m_pSockRecord->socket());
198  m_pSockRecord->invalidate();
199  return -1;
200  }
201  }
202 #endif
203  }
204  // Set the timeout for the socket
205 #ifdef _WIN32
206  // On Windows timeout is a DWORD in milliseconds
207  // (https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-setsockopt)
208  iResult = ::setsockopt(
209  m_pSockRecord->socket(), SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeoutMs), sizeof(DWORD));
210 #else
211  iResult = ::setsockopt(
212  m_pSockRecord->socket(), SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&tv), sizeof(struct timeval));
213 #endif
214  return iResult;
215 }
216 
218 {
219  // Close the socket when finished receiving datagrams
220 #ifdef _WIN32
221  ::closesocket(m_pSockRecord->socket());
222  ::WSACleanup();
223 #else
224  ::close(m_pSockRecord->socket());
225 #endif
226  m_pSockRecord->invalidate();
227  return 0;
228 }
229 
230 ITransport::send_return_t TcpSocket::send(const char* pData, size_t size)
231 {
232  const bufsize_t eff_buflen = castClamped<bufsize_t>(size);
233  // send buffer via TCP socket
234  return ::send(m_pSockRecord->socket(), pData, eff_buflen, 0);
235 }
236 
237 ITransport::recv_return_t TcpSocket::recv(ByteBuffer& buffer, std::size_t maxBytesToReceive)
238 {
239  const bufsize_t eff_maxsize = castClamped<bufsize_t>(maxBytesToReceive);
240 
241  // receive from TCP Socket
242  buffer.resize(static_cast<std::size_t>(eff_maxsize));
243  char* pBuffer = reinterpret_cast<char*>(buffer.data());
244 
245  const ITransport::recv_return_t retval = ::recv(m_pSockRecord->socket(), pBuffer, eff_maxsize, 0);
246 
247  if (retval >= 0)
248  {
249  buffer.resize(static_cast<size_t>(retval));
250  }
251 
252  return retval;
253 }
254 
255 ITransport::recv_return_t TcpSocket::read(ByteBuffer& buffer, std::size_t nBytesToReceive)
256 {
257  // receive from TCP Socket
258  try
259  {
260  buffer.resize(nBytesToReceive);
261  }
262  catch (std::length_error&)
263  {
264  // Supress warning for sizes >= 125MB, because it is very likely an invalid
265  // size
266  if (nBytesToReceive < 1024u * 1024u * 125u)
267  {
268  std::cout << "TcpSocket::" << __FUNCTION__ << ": Unable to allocate buffer of size " << nBytesToReceive
269  << std::endl;
270  }
271  return -1;
272  }
273 
274  char* const pBufferStart = reinterpret_cast<char*>(buffer.data());
275  char* pBuffer = pBufferStart;
276 
277  while (nBytesToReceive > 0)
278  {
279  const bufsize_t eff_maxsize = castClamped<bufsize_t>(nBytesToReceive);
280 
281  const ITransport::recv_return_t bytesReceived = ::recv(m_pSockRecord->socket(), pBuffer, eff_maxsize, 0);
282 
283  if (bytesReceived == SOCKET_ERROR)
284  {
285  return -1;
286  }
287  else if (bytesReceived == 0)
288  {
289  // stream was properly closed
290  break;
291  }
292  pBuffer += bytesReceived;
293  nBytesToReceive -= static_cast<size_t>(bytesReceived);
294  }
295 
296  buffer.resize(static_cast<size_t>(pBuffer - pBufferStart));
297 
298  return static_cast<ITransport::recv_return_t>(buffer.size());
299 }
300 
302 {
303  int error_code;
304 #ifdef _WIN32
305  int error_code_size = sizeof(int);
306  if (::getsockopt(
307  m_pSockRecord->socket(), SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error_code), &error_code_size)
308  != 0)
309  {
310  std::cout << "Error getting error code" << std::endl;
311  }
312 #else
313  socklen_t error_code_size = sizeof error_code;
314  if (::getsockopt(m_pSockRecord->socket(), SOL_SOCKET, SO_ERROR, &error_code, &error_code_size) != 0)
315  {
316  std::cout << "Error getting error code" << std::endl;
317  }
318 #endif
319  return error_code;
320 }
321 
322 } // namespace visionary
SOCKET_ERROR
#define SOCKET_ERROR
Definition: SockRecord.h:30
TcpSocket.h
visionary::bufsize_t
size_t bufsize_t
Definition: TcpSocket.cpp:21
visionary::TcpSocket::getLastError
int getLastError() override
Definition: TcpSocket.cpp:301
visionary::TcpSocket::recv
recv_return_t recv(ByteBuffer &buffer, std::size_t maxBytesToReceive) override
Definition: TcpSocket.cpp:237
visionary::ITransport::recv_return_t
ssize_t recv_return_t
Definition: ITransport.h:29
visionary
Definition: MD5.cpp:44
SockRecord.h
visionary::ITransport::ByteBuffer
std::vector< std::uint8_t > ByteBuffer
Definition: ITransport.h:22
visionary::TcpSocket::TcpSocket
TcpSocket()
Definition: TcpSocket.cpp:24
visionary::TcpSocket::shutdown
int shutdown() override
Definition: TcpSocket.cpp:217
visionary::SockRecord
Definition: SockRecord.h:35
visionary::TcpSocket::send
send_return_t send(const char *pData, size_t size) override
Definition: TcpSocket.cpp:230
INVALID_SOCKET
#define INVALID_SOCKET
Definition: SockRecord.h:29
visionary::TcpSocket::read
recv_return_t read(ByteBuffer &buffer, std::size_t nBytesToReceive) override
Definition: TcpSocket.cpp:255
visionary::TcpSocket::m_pSockRecord
std::unique_ptr< SockRecord > m_pSockRecord
Definition: TcpSocket.h:44
visionary::TcpSocket::~TcpSocket
virtual ~TcpSocket()
Definition: TcpSocket.cpp:28
visionary::ITransport::send_return_t
ssize_t send_return_t
Definition: ITransport.h:28
SOCKET
int SOCKET
Definition: SockRecord.h:28
errno
#define errno
Definition: borland.hpp:85
NO_ERROR
NO_ERROR
boost::multi_index::safe_mode::error_code
error_code
Definition: safe_mode_errors.hpp:28
visionary::TcpSocket::connect
int connect(const std::string &ipaddr, std::uint16_t port, std::uint32_t timeoutMs=5000)
Definition: TcpSocket.cpp:36
NumericConv.h


sick_visionary_ros
Author(s): SICK AG TechSupport 3D Snapshot
autogenerated on Thu Feb 8 2024 03:48:42