libvisiontransfer  5.0.1
imagetransfer.cpp
1 /*******************************************************************************
2  * Copyright (c) 2017 Nerian Vision Technologies
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 <cstdio>
16 #include <iostream>
17 #include <cstring>
18 #include <memory>
19 #include <fcntl.h>
20 #include <string>
21 #include <vector>
22 #include "visiontransfer/imagetransfer.h"
23 #include "visiontransfer/exceptions.h"
24 #include "visiontransfer/datablockprotocol.h"
25 
26 
27 #ifdef _MSC_VER
28  // Visual studio does not come with snprintf
29  #define snprintf _snprintf_s
30 #endif
31 
32 // Network headers
33 #ifdef _WIN32
34  #ifndef _WIN32_WINNT
35  #define _WIN32_WINNT 0x501
36  #endif
37  #define _WINSOCK_DEPRECATED_NO_WARNINGS
38 
39  #ifndef NOMINMAX
40  #define NOMINMAX
41  #endif
42 
43  #include <winsock2.h>
44  #include <ws2tcpip.h>
45 
46  // Some defines to make windows socket look more like
47  // posix sockets.
48  #ifdef EWOULDBLOCK
49  #undef EWOULDBLOCK
50  #endif
51  #ifdef ECONNRESET
52  #undef ECONNRESET
53  #endif
54  #ifdef ETIMEDOUT
55  #undef ETIMEDOUT
56  #endif
57 
58  #define EWOULDBLOCK WSAEWOULDBLOCK
59  #define ECONNRESET WSAECONNRESET
60  #define ETIMEDOUT WSAETIMEDOUT
61  #define MSG_DONTWAIT 0
62 
63  #define close closesocket
64 
65  // Emulate posix errno. Does not work in a throw
66  // statement (WTF?)
67  #undef errno
68  #define errno WSAGetLastError()
69  #define strerror win_strerror
70 
71  std::string win_strerror(unsigned long error) {
72  char* str = nullptr;
73  if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
74  FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
75  nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
76  (LPSTR)&str, 0, nullptr) == 0 || str == nullptr) {
77  return "Unknown error";
78  } else {
79  char buffer[512];
80  snprintf(buffer, sizeof(buffer), "%s (%lu)", str, error);
81  LocalFree(str);
82  return std::string(buffer);
83  }
84  }
85 
86 #else
87  #include <arpa/inet.h>
88  #include <netinet/tcp.h>
89  #include <sys/types.h>
90  #include <sys/socket.h>
91  #include <netdb.h>
92  #include <netinet/in.h>
93  #include <errno.h>
94  #include <unistd.h>
95  #include <signal.h>
96 
97  // Unfortunately we have to use a winsock like socket type
98  typedef int SOCKET;
99  #define INVALID_SOCKET -1
100 
101  // Also we need some additional winsock defines
102  #define WSA_IO_PENDING 0
103 #endif
104 
105 using namespace std;
106 
107 /*************** Pimpl class containing all private members ***********/
108 
109 class ImageTransfer::Pimpl {
110 public:
111  Pimpl(OperationMode mode, const char* remoteAddress, const char* remoteService,
112  const char* localAddress, const char* localService, int bufferSize,
113  int maxUdpPacketSize);
114  ~Pimpl();
115 
116  // Redeclaration of public members
117  void setRawTransferData(const ImagePair& metaData, unsigned char* rawData,
118  int secondTileWidth = 0, int validBytes = 0x7FFFFFFF);
119  void setRawValidBytes(int validBytes);
120  void setTransferImagePair(const ImagePair& imagePair);
121  TransferStatus transferData(bool block);
122  bool receiveImagePair(ImagePair& imagePair, bool block);
123  bool receivePartialImagePair(ImagePair& imagePair, int& validRows, bool& complete, bool block);
124  bool tryAccept();
125  bool isClientConnected() const;
126  void disconnect();
127  std::string getClientAddress() const;
128 
129 private:
130  static constexpr int UDP_BUFFERS = 256;
131  static constexpr int TCP_BUFFER_SIZE = 0xFFFF; //64K - 1
132 
133  // The chosen operation mode for this connection
134  OperationMode mode;
135 
136  // Maximum packet size for UPD transfers
137  int maxUdpPacketSize;
138 
139  // The socket file descriptor
140  SOCKET socket;
141 
142  // In server mode: Socket listening on the server port
143  SOCKET serverSocket;
144 
145  // In server mode: Address if the connected client
146  sockaddr_in clientAddress;
147 
148  // Address for UDP transmissions
149  sockaddr_in udpAddress;
150 
151  // Object for encoding and decoding the network protocol
152  std::unique_ptr<ImageProtocol> protocol;
153 
154  // Outstanding network message that still has to be transferred
155  int currentMsgLen;
156  int currentMsgOffset;
157  const unsigned char* currentMsg;
158 
159  // Size of the socket buffers
160  int bufferSize;
161 
162  // Buffered error state the current reception
163  bool receptionFailed;
164 
165  bool socketIsBlocking;
166 
167  // Sets some required socket options for TCP sockets
168  void setSocketOptions();
169 
170  // Initialization functions for different operation modes
171  void initTcpClient(const addrinfo& remoteAddressInfo, const addrinfo& localAddressInfo);
172  void initTcpServer(const addrinfo& localAddressInfo);
173  void initUdp(const addrinfo& remoteAddressInfo, const addrinfo& localAddressInfo);
174 
175  // Receives some network data and forwards it to the protocol
176  int receiveSingleNetworkMessages(bool block);
177  bool receiveNetworkData(bool block);
178 
179  void win32SetBlocking(bool block);
180 };
181 
182 /******************** Stubs for all public members ********************/
183 
184 ImageTransfer::ImageTransfer(OperationMode mode, const char* remoteAddress,
185  const char* remoteService, const char* localAddress, const char* localService, int bufferSize,
186  int maxUdpPacketSize):
187  pimpl(new Pimpl(mode, remoteAddress, remoteService, localAddress, localService, bufferSize,
188  maxUdpPacketSize)) {
189  // All initialization in the pimpl class
190 }
191 
192 ImageTransfer::~ImageTransfer() {
193  delete pimpl;
194 }
195 
196 void ImageTransfer::setRawTransferData(const ImagePair& metaData, unsigned char* rawData,
197  int secondTileWidth, int validBytes) {
198  pimpl->setRawTransferData(metaData, rawData, secondTileWidth, validBytes);
199 }
200 
201 void ImageTransfer::setRawValidBytes(int validBytes) {
202  pimpl->setRawValidBytes(validBytes);
203 }
204 
206  pimpl->setTransferImagePair(imagePair);
207 }
208 
210  return pimpl->transferData(block);
211 }
212 
213 bool ImageTransfer::receiveImagePair(ImagePair& imagePair, bool block) {
214  return pimpl->receiveImagePair(imagePair, block);
215 }
216 
217 bool ImageTransfer::receivePartialImagePair(ImagePair& imagePair, int& validRows, bool& complete, bool block) {
218  return pimpl->receivePartialImagePair(imagePair, validRows, complete, block);
219 }
220 
222  return pimpl->tryAccept();
223 }
224 
226  return pimpl->isClientConnected();
227 }
228 
230  pimpl->disconnect();
231 }
232 
233 std::string ImageTransfer::getClientAddress() const {
234  return pimpl->getClientAddress();
235 }
236 
237 /******************** Implementation in pimpl class *******************/
238 
239 ImageTransfer::Pimpl::Pimpl(OperationMode mode, const char* remoteAddress, const char* remoteService,
240  const char* localAddress, const char* localService, int bufferSize, int maxUdpPacketSize)
241  : mode(mode), maxUdpPacketSize(maxUdpPacketSize), socket(INVALID_SOCKET), serverSocket(INVALID_SOCKET), currentMsgLen(0),
242  currentMsgOffset(0), currentMsg(nullptr), bufferSize(bufferSize), receptionFailed(false), socketIsBlocking(true) {
243 
244 #ifdef _WIN32
245  // In windows, we first have to initialize winsock
246  WSADATA wsaData;
247  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
248  throw TransferException("WSAStartup failed!");
249  }
250 #else
251  // We don't want to be interrupted by the pipe signal
252  signal(SIGPIPE, SIG_IGN);
253 #endif
254 
255  // If address is null we use the any address
256  if(remoteAddress == nullptr || string(remoteAddress) == "") {
257  remoteAddress = "0.0.0.0";
258  }
259  if(localAddress == nullptr || string(localAddress) == "") {
260  localAddress = "0.0.0.0";
261  }
262 
263  // Resolve address
264  addrinfo hints;
265  memset(&hints, 0, sizeof(hints));
266  hints.ai_family = AF_INET; // Use IPv4
267  hints.ai_socktype = (mode == TCP_CLIENT || mode == TCP_SERVER) ? SOCK_STREAM : SOCK_DGRAM;
268  hints.ai_flags = 0;
269  hints.ai_protocol = 0;
270 
271  addrinfo* remoteAddressInfo = nullptr;
272  if(getaddrinfo(remoteAddress, remoteService, &hints, &remoteAddressInfo) != 0 || remoteAddressInfo == nullptr) {
273  TransferException ex("Error resolving remote address: " + string(strerror(errno)));
274  throw ex;
275  }
276 
277  addrinfo* localAddressInfo = nullptr;
278  if(getaddrinfo(localAddress, localService, &hints, &localAddressInfo) != 0 || localAddressInfo == nullptr) {
279  TransferException ex("Error resolving local address: " + string(strerror(errno)));
280  throw ex;
281  }
282 
283  // Perform initialization depending on the selected operation mode
284  try {
285  switch(mode) {
286  case TCP_CLIENT: initTcpClient(*remoteAddressInfo, *localAddressInfo); break;
287  case TCP_SERVER: initTcpServer(*localAddressInfo); break;
288  case UDP: initUdp(*remoteAddressInfo, *localAddressInfo); break;
289  default: throw TransferException("Illegal operation mode");
290  }
291  } catch(...) {
292  freeaddrinfo(remoteAddressInfo);
293  freeaddrinfo(localAddressInfo);
294  throw;
295  }
296 
297  freeaddrinfo(remoteAddressInfo);
298  freeaddrinfo(localAddressInfo);
299 }
300 
301 ImageTransfer::Pimpl::~Pimpl() {
302  if(socket != INVALID_SOCKET) {
303  close(socket);
304  }
305 
306  if(serverSocket != INVALID_SOCKET) {
307  close(serverSocket);
308  }
309 }
310 
311 void ImageTransfer::Pimpl::initTcpClient(const addrinfo& remoteAddressInfo, const addrinfo& localAddressInfo) {
312  protocol.reset(new ImageProtocol(ImageProtocol::PROTOCOL_TCP));
313 
314  // Connect
315  socket = ::socket(remoteAddressInfo.ai_family, remoteAddressInfo.ai_socktype,
316  remoteAddressInfo.ai_protocol);
317  if(socket == INVALID_SOCKET) {
318  TransferException ex("Error creating socket: " + string(strerror(errno)));
319  throw ex;
320  }
321 
322  // Enable reuse address
323  int enable = 1;
324  setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
325 
326  // Bind to local port
327  if (::bind(socket, localAddressInfo.ai_addr, static_cast<int>(localAddressInfo.ai_addrlen)) < 0) {
328  TransferException ex("Error binding socket: " + string(strerror(errno)));
329  throw ex;
330  }
331 
332  // Perform connection
333  if(connect(socket, remoteAddressInfo.ai_addr, static_cast<int>(remoteAddressInfo.ai_addrlen)) < 0) {
334  TransferException ex("Error connection to destination address: " + string(strerror(errno)));
335  throw ex;
336  }
337 
338  // Set special socket options
339  setSocketOptions();
340 }
341 
342 void ImageTransfer::Pimpl::initTcpServer(const addrinfo& localAddressInfo) {
343  protocol.reset(new ImageProtocol(ImageProtocol::PROTOCOL_TCP));
344 
345  // Create socket
346  serverSocket = ::socket(localAddressInfo.ai_family, localAddressInfo.ai_socktype,
347  localAddressInfo.ai_protocol);
348  if (serverSocket == INVALID_SOCKET) {
349  TransferException ex("Error opening socket: " + string(strerror(errno)));
350  throw ex;
351  }
352 
353  // Enable reuse address
354  int enable = 1;
355  setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
356 
357  // Open a server port
358  if (::bind(serverSocket, localAddressInfo.ai_addr, static_cast<int>(localAddressInfo.ai_addrlen)) < 0) {
359  TransferException ex("Error binding socket: " + string(strerror(errno)));
360  throw ex;
361  }
362 
363  // Make the server socket non-blocking
364 #ifdef _WIN32
365  unsigned long on = 1;
366  ioctlsocket(serverSocket, FIONBIO, &on);
367 #else
368  fcntl(serverSocket, F_SETFL, O_NONBLOCK);
369 #endif
370 
371  // Listen on port
372  listen(serverSocket, 1);
373 }
374 
375 void ImageTransfer::Pimpl::initUdp(const addrinfo& remoteAddressInfo, const addrinfo& localAddressInfo) {
376  protocol.reset(new ImageProtocol(ImageProtocol::PROTOCOL_UDP, maxUdpPacketSize));
377  // Create socket
378  socket = ::socket(localAddressInfo.ai_family, localAddressInfo.ai_socktype,
379  localAddressInfo.ai_protocol);
380  if(socket == INVALID_SOCKET) {
381  TransferException ex("Error creating socket: " + string(strerror(errno)));
382  throw ex;
383  }
384 
385  // Enable reuse address
386  int enable = 1;
387  setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
388 
389  // Bind socket to port
390  if (::bind(socket, localAddressInfo.ai_addr, static_cast<int>(localAddressInfo.ai_addrlen)) < 0) {
391  TransferException ex("Error binding socket: " + string(strerror(errno)));
392  throw ex;
393  }
394 
395  // Store remote address
396  if(remoteAddressInfo.ai_addrlen != sizeof(udpAddress)) {
397  throw TransferException("Illegal address length");
398  }
399  memcpy(&udpAddress, remoteAddressInfo.ai_addr, sizeof(udpAddress));
400 
401  // Set special socket options
402  setSocketOptions();
403 }
404 
405 bool ImageTransfer::Pimpl::tryAccept() {
406  if(mode != TCP_SERVER) {
407  throw TransferException("Connections can only be accepted in tcp server mode");
408  }
409 
410  // Accept one connection
411  socklen_t clientAddressLength = sizeof(clientAddress);
412 
413  SOCKET newSocket = accept(serverSocket,
414  reinterpret_cast<sockaddr *>(&clientAddress),
415  &clientAddressLength);
416 
417  if(newSocket == INVALID_SOCKET) {
418  if(errno == EWOULDBLOCK || errno == ETIMEDOUT) {
419  // No connection
420  return false;
421  } else {
422  TransferException ex("Error accepting connection: " + string(strerror(errno)));
423  throw ex;
424  }
425  }
426 
427  // Close old socket and set new socket
428  if(socket != INVALID_SOCKET) {
429  close(socket);
430  }
431  socket = newSocket;
432 
433  // Set special socket options
434  setSocketOptions();
435 
436  // Reset connection data
437  protocol->resetTransfer();
438  protocol->resetReception();
439 
440  return true;
441 }
442 
443 std::string ImageTransfer::Pimpl::getClientAddress() const {
444  if(socket == INVALID_SOCKET) {
445  return ""; // No client connected
446  }
447 
448  char strPort[11];
449  snprintf(strPort, sizeof(strPort), ":%d", clientAddress.sin_port);
450 
451  return string(inet_ntoa(clientAddress.sin_addr)) + strPort;
452 }
453 
454 void ImageTransfer::Pimpl::setSocketOptions() {
455  if(mode == TCP_SERVER) {
456  // Make sure the client socket didn't inherit the non-blocking mode from the server socket
457 #ifdef _WIN32
458  unsigned long on = 0;
459  ioctlsocket(socket, FIONBIO, &on);
460 #else
461  fcntl(socket, F_SETFL, 0);
462 #endif
463  }
464 
465  // Set the socket buffer sizes
466  if(bufferSize > 0) {
467  setsockopt(socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
468  setsockopt(socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
469  }
470 
471  // Set sending and receive timeouts
472 #ifdef _WIN32
473  unsigned int timeout = 1000;
474 #else
475  struct timeval timeout;
476  timeout.tv_sec = 1;
477  timeout.tv_usec = 0;
478 #endif
479 
480  setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
481  setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
482 
483  // Disable multicast loops for improved performance
484  unsigned char loop = 0;
485  setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char*>(&loop), sizeof(loop));
486 
487  // Try to set a more suitable congestion control algorithm for TCP streams
488 #ifdef TCP_CONGESTION
489  if(mode == TCP_SERVER || mode == TCP_CLIENT) {
490  char optval[16];
491  strcpy(optval, "westwood");
492  if (setsockopt(socket, IPPROTO_TCP, TCP_CONGESTION, optval, strlen(optval)) < 0) {
493  // Can't set westwood. Let's try reno
494  strcpy(optval, "reno");
495  setsockopt(socket, IPPROTO_TCP, TCP_CONGESTION, optval, strlen(optval));
496  }
497  }
498 #endif
499 }
500 
501 void ImageTransfer::Pimpl::setRawTransferData(const ImagePair& metaData,
502  unsigned char* rawData, int secondTileWidth, int validBytes) {
503  protocol->setRawTransferData(metaData, rawData, secondTileWidth, validBytes);
504  currentMsg = nullptr;
505 }
506 
507 void ImageTransfer::Pimpl::setRawValidBytes(int validBytes) {
508  protocol->setRawValidBytes(validBytes);
509 }
510 
511 void ImageTransfer::Pimpl::setTransferImagePair(const ImagePair& imagePair) {
512  protocol->setTransferImagePair(imagePair);
513  currentMsg = nullptr;
514 }
515 
516 void ImageTransfer::Pimpl::win32SetBlocking(bool block) {
517 #ifdef _WIN32
518  if(block != socketIsBlocking) {
519  // Windows doesn't support MSG_DONTWAIT. Have to touch the socket
520  unsigned long on = block ? 0 : 1;
521  ioctlsocket(socket, FIONBIO, &on);
522 
523  socketIsBlocking = block;
524  }
525 #endif
526 }
527 
528 ImageTransfer::TransferStatus ImageTransfer::Pimpl::transferData(bool block) {
529  if(currentMsg == nullptr) {
530  currentMsgOffset = 0;
531  currentMsg = protocol->getTransferMessage(currentMsgLen);
532 
533  if(currentMsg == nullptr) {
534  return NO_VALID_DATA;
535  }
536  }
537 
538  while(currentMsg != nullptr) {
539  int writing = (int)(currentMsgLen - currentMsgOffset);
540  int written = 0;
541 
542  win32SetBlocking(block);
543 
544  switch(mode) {
545  case TCP_SERVER:
546  case TCP_CLIENT:
547  written = send(socket, reinterpret_cast<const char*>(&currentMsg[currentMsgOffset]), writing,
548  block ? 0 : MSG_DONTWAIT);
549  break;
550  case UDP:
551  written = sendto(socket, reinterpret_cast<const char*>(&currentMsg[currentMsgOffset]), writing,
552  block ? 0 : MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&udpAddress), sizeof(udpAddress));
553  break;
554  }
555 
556  unsigned long sendError = errno;
557 
558  if(written < 0) {
559  if(!block && (sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT)) {
560  // The socket is not yet ready for a new transfer
561  return WOULD_BLOCK;
562  }
563  else if(sendError == ECONNRESET && (mode == TCP_SERVER || mode == TCP_CLIENT)) {
564  // Connection closed by remote host
565  close(socket);
566  socket = INVALID_SOCKET;
567  return CONNECTION_CLOSED;
568  } else {
569  TransferException ex("Error sending message: " + string(strerror(sendError)));
570  throw ex;
571  }
572  } else if(written != writing) {
573  // The message has been transmitted partially
574  if(mode == UDP) {
575  throw TransferException("Unable to transmit complete UDP message");
576  }
577 
578  currentMsgOffset+= written;
579  if(!block) {
580  return PARTIAL_TRANSFER;
581  }
582  } else if(!block) {
583  // Only one iteration allowed in non-blocking mode
584  currentMsg = nullptr;
585  break;
586  } else {
587  // Get next message
588  currentMsg = protocol->getTransferMessage(currentMsgLen);
589  currentMsgOffset = 0;
590  }
591  }
592 
593  if(mode == TCP_SERVER && protocol->transferComplete()) {
594  // Force a flush by turning the nagle algorithm off and on
595  int flag = 1;
596  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
597  flag = 0;
598  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
599  }
600 
601  if(protocol->transferComplete()) {
602  return ALL_TRANSFERRED;
603  } else {
604  return PARTIAL_TRANSFER;
605  }
606 }
607 
608 bool ImageTransfer::Pimpl::receiveImagePair(ImagePair& imagePair, bool block) {
609  int validRows = 0;
610  bool complete = false;
611 
612  while(!complete) {
613  if(!receivePartialImagePair(imagePair, validRows, complete, block)) {
614  return false;
615  }
616  }
617 
618  return true;
619 }
620 
621 bool ImageTransfer::Pimpl::receivePartialImagePair(ImagePair& imagePair,
622  int& validRows, bool& complete, bool block) {
623  if(receptionFailed) {
624  // Notify about reception errors by returning false once
625  receptionFailed = false;
626  return false;
627  }
628 
629  // Try to receive further image data
630  while(!protocol->imagesReceived() && receiveNetworkData(block)) {
631  block = false;
632  }
633 
634  // Get received image
635  return protocol->getPartiallyReceivedImagePair(imagePair, validRows, complete);
636 }
637 
638 int ImageTransfer::Pimpl::receiveSingleNetworkMessages(bool block) {
639  int maxLength = 0;
640  char* buffer = reinterpret_cast<char*>(protocol->getNextReceiveBuffer(maxLength));
641 
642  int bytesReceived = recv(socket, buffer, maxLength,
643 #ifdef _WIN32
644  0
645 #else
646  block ? 0 : MSG_DONTWAIT
647 #endif
648  );
649 
650  if(bytesReceived > 0) {
651  if(!protocol->processReceivedMessage(bytesReceived)) {
652  if(mode == TCP_CLIENT) {
653  receptionFailed = true;
654  }
655  }
656  }
657 
658  return bytesReceived;
659 }
660 
661 bool ImageTransfer::Pimpl::receiveNetworkData(bool block) {
662  win32SetBlocking(block);
663 
664  int received = receiveSingleNetworkMessages(block);
665  bool ret = true;
666 
667  if(received == 0 && (mode == TCP_SERVER || mode == TCP_CLIENT)) {
668  // Connection closed by remote host
669  close(socket);
670  socket = INVALID_SOCKET;
671  ret = false;
672  } else if(received < 0) {
673  if(errno == EWOULDBLOCK || errno == EINTR || errno == ETIMEDOUT || errno == WSA_IO_PENDING) {
674  // Reception was cancelled because it took too long,
675  // or because of some signal. We reset reception for the current frame.
676  ret = false;
677  } else {
678  TransferException ex("Error reading from socket: " + string(strerror(errno)));
679  throw ex;
680  }
681  }
682 
683  return ret;
684 }
685 
686 void ImageTransfer::Pimpl::disconnect() {
687  if(mode != TCP_SERVER && mode != TCP_CLIENT) {
688  throw TransferException("Only TCP transfers can be disconnected");
689  }
690  if(socket != INVALID_SOCKET) {
691  close(socket);
692  socket = INVALID_SOCKET;
693  }
694 }
695 
696 bool ImageTransfer::Pimpl::isClientConnected() const {
697  return socket != INVALID_SOCKET;
698 }
bool receivePartialImagePair(ImagePair &imagePair, int &validRows, bool &complete, bool block=false)
Returns the received image pair, even if it is not yet complete.
TransferStatus transferData(bool block)
Performs a partial (or full) image transmission.
The connection has been closed by the remote host.
Definition: imagetransfer.h:48
Using TCP and acting as communication server.
Definition: imagetransfer.h:42
A lightweight protocol for transferring image pairs.
Definition: imageprotocol.h:36
OperationMode
Supported transfer modes.
Definition: imagetransfer.h:33
Using TCP and acting as communication client.
Definition: imagetransfer.h:39
ImageTransfer(OperationMode mode, const char *remoteAddress, const char *remoteService, const char *localAddress, const char *localService, int bufferSize=1048576, int maxUdpPacketSize=1472)
Creates a new transfer object.
bool tryAccept()
Tries to accept a client connection.
void setTransferImagePair(const ImagePair &imagePair)
Sets a new image pair that shall be transmitted.
void setRawTransferData(const ImagePair &metaData, unsigned char *rawData, int secondTileWidth=0, int validBytes=0x7FFFFFFF)
Sets the raw pixel data for a partial image transmission.
The connection-less UDP transport protocol.
Definition: imageprotocol.h:44
Exception class that is used for all transfer exceptions.
Definition: exceptions.h:31
The image pair has been transferred completely.
Definition: imagetransfer.h:51
TransferStatus
The result of a partial image transfer.
Definition: imagetransfer.h:46
A set of two images, which are usually the left camera image and the disparity map.
Definition: imagepair.h:30
bool isClientConnected() const
Returns true if a client is connected.
void disconnect()
Terminates the current connection.
The operation would block and blocking as been disabled.
Definition: imagetransfer.h:61
void setRawValidBytes(int validBytes)
Updates the number of valid bytes in a partial raw transmission.
std::string getClientAddress() const
Returns the address of the connected client.
bool receiveImagePair(ImagePair &imagePair, bool block=true)
Waits for and receives a new image pair.
There is currently no more data that could be transmitted.
Definition: imagetransfer.h:58
The connection oriented TCP transport protocol.
Definition: imageprotocol.h:41
Nerian Vision Technologies