libvisiontransfer  10.6.0
networking.cpp
1 /*******************************************************************************
2  * Copyright (c) 2023 Allied Vision Technologies GmbH
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *******************************************************************************/
14 
15 #include "visiontransfer/networking.h"
16 #include "visiontransfer/exceptions.h"
17 #include <cstring>
18 #include <fcntl.h>
19 
20 using namespace std;
21 using namespace visiontransfer;
22 using namespace visiontransfer::internal;
23 
24 namespace visiontransfer {
25 namespace internal {
26 
27 // System-specific error message wrappers
28 #ifdef _WIN32
29  error_int_type Networking::getErrno() {
30  return WSAGetLastError();
31  }
32 
33  std::string Networking::getErrorString(error_int_type error) {
34  char* str = nullptr;
35  if(FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
36  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
37  nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
38  (LPSTR)&str, 0, nullptr) == 0 || str == nullptr) {
39  return "Unknown error";
40  } else {
41  char buffer[512];
42  snprintf(buffer, sizeof(buffer), "%s (%lu)", str, error);
43  LocalFree(str);
44  return std::string(buffer);
45  }
46  }
47 #else
48  error_int_type Networking::getErrno() {
49  return errno;
50  }
51  std::string Networking::getErrorString(error_int_type error) {
52  return std::string(strerror(error));
53  }
54 #endif
55 
56 std::string Networking::getLastErrorString() {
57  return getErrorString(getErrno());
58 }
59 
60 void Networking::initNetworking() {
61 #ifdef _WIN32
62  // In windows, we first have to initialize winsock
63  WSADATA wsaData;
64  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
65  throw TransferException("WSAStartup failed!");
66  }
67 #endif
68 }
69 
70 addrinfo* Networking::resolveAddress(const char* address, const char* service) {
71  addrinfo hints;
72  memset(&hints, 0, sizeof(hints));
73  hints.ai_family = AF_INET; // Use IPv4
74  hints.ai_socktype = SOCK_STREAM;
75  hints.ai_flags = 0;
76  hints.ai_protocol = 0;
77 
78  addrinfo* addressInfo = nullptr;
79 
80  if(getaddrinfo(address, service, &hints, &addressInfo) != 0 || addressInfo == nullptr) {
81  TransferException ex("Error resolving address: " + getLastErrorString());
82  throw ex;
83  }
84 
85  if(addressInfo->ai_addrlen != sizeof(sockaddr_in)) {
86  throw TransferException("Illegal address length");
87  }
88 
89  return addressInfo;
90 }
91 
92 SOCKET Networking::connectTcpSocket(const addrinfo* address) {
93  SOCKET sock = ::socket(address->ai_family, address->ai_socktype,
94  address->ai_protocol);
95  if(sock == INVALID_SOCKET) {
96  TransferException ex("Error creating socket: " + getLastErrorString());
97  throw ex;
98  }
99 
100  if(connect(sock, address->ai_addr, static_cast<int>(address->ai_addrlen)) < 0) {
101  TransferException ex("Error connection to destination address: " + getLastErrorString());
102  throw ex;
103  }
104 
105  return sock;
106 }
107 
108 void Networking::setSocketTimeout(SOCKET socket, int timeoutMillisec) {
109 #ifdef _WIN32
110  unsigned int timeout = timeoutMillisec;
111 #else
112  struct timeval timeout;
113  timeout.tv_sec = timeoutMillisec/1000;
114  timeout.tv_usec = timeoutMillisec*1000;
115 #endif
116 
117  setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
118  setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
119 }
120 
121 void Networking::closeSocket(SOCKET& socket) {
122  setSocketBlocking(socket, false);
123  shutdown(socket, SHUT_WR);
124 
125  // Receive remaining data
126  char buffer[1024];
127  for(int i=0; i<3; i++) {
128  int received = recv(socket, buffer, sizeof(buffer), 0);
129  if(received <= 0) {
130  break;
131  }
132  }
133 
134  close(socket);
135  socket = INVALID_SOCKET;
136 }
137 
138 void Networking::setSocketBlocking(SOCKET socket, bool blocking) {
139 #ifdef _WIN32
140  unsigned long on = (blocking ? 0 : 1);
141  ioctlsocket(socket, FIONBIO, &on);
142 #else
143  int flags = fcntl(socket, F_GETFL, 0);
144  if(flags != -1) {
145  if(blocking) {
146  flags &= ~O_NONBLOCK;
147  } else {
148  flags |= O_NONBLOCK;
149  }
150  fcntl(socket, F_SETFL, flags);
151  }
152 #endif
153 }
154 
155 void Networking::enableReuseAddress(SOCKET socket, bool reuse) {
156  int enable = reuse ? 1 : 0;
157  setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
158 }
159 
160 void Networking::bindSocket(SOCKET socket, const addrinfo* addressInfo) {
161  if (::bind(socket, addressInfo->ai_addr, static_cast<int>(addressInfo->ai_addrlen)) < 0) {
162  TransferException ex("Error binding socket: " + getLastErrorString());
163  throw ex;
164  }
165 }
166 
167 SOCKET Networking::acceptConnection(SOCKET socket, sockaddr_in& remoteAddress) {
168  socklen_t clientAddressLength = sizeof(sockaddr_in);
169 
170  SOCKET newSocket = accept(socket, reinterpret_cast<sockaddr *>(&remoteAddress),
171  &clientAddressLength);
172 
173  if(clientAddressLength != sizeof(sockaddr_in)) {
174  throw TransferException("Received network address with invalid length");
175  }
176 
177  if(newSocket == INVALID_SOCKET) {
178  if(getErrno() == EWOULDBLOCK || getErrno() == ETIMEDOUT) {
179  // No connection
180  return INVALID_SOCKET;
181  } else {
182  TransferException ex("Error accepting connection: " + getLastErrorString());
183  throw ex;
184  }
185  }
186 
187  return newSocket;
188 }
189 
190 }} // namespace
191 
visiontransfer::TransferException
Exception class that is used for all transfer exceptions.
Definition: exceptions.h:45
Allied Vision