libvisiontransfer  5.1.0
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(remoteService != nullptr && string(remoteService) != "") {
273  if(getaddrinfo(remoteAddress, remoteService, &hints, &remoteAddressInfo) != 0 || remoteAddressInfo == nullptr) {
274  TransferException ex("Error resolving remote address: " + string(strerror(errno)));
275  throw ex;
276  }
277  }
278 
279  addrinfo* localAddressInfo = nullptr;
280  if(localService != nullptr && string(localService) != "") {
281  if(getaddrinfo(localAddress, localService, &hints, &localAddressInfo) != 0 || localAddressInfo == nullptr) {
282  TransferException ex("Error resolving local address: " + string(strerror(errno)));
283  throw ex;
284  }
285  }
286 
287  // Perform initialization depending on the selected operation mode
288  try {
289  switch(mode) {
290  case TCP_CLIENT: initTcpClient(remoteAddressInfo, localAddressInfo); break;
291  case TCP_SERVER: initTcpServer(localAddressInfo); break;
292  case UDP: initUdp(remoteAddressInfo, localAddressInfo); break;
293  default: throw TransferException("Illegal operation mode");
294  }
295  } catch(...) {
296  freeaddrinfo(remoteAddressInfo);
297  freeaddrinfo(localAddressInfo);
298  throw;
299  }
300 
301  if(remoteAddressInfo != nullptr) {
302  freeaddrinfo(remoteAddressInfo);
303  }
304  if(localAddressInfo != nullptr) {
305  freeaddrinfo(localAddressInfo);
306  }
307 }
308 
309 ImageTransfer::Pimpl::~Pimpl() {
310  if(socket != INVALID_SOCKET) {
311  close(socket);
312  }
313 
314  if(serverSocket != INVALID_SOCKET) {
315  close(serverSocket);
316  }
317 }
318 
319 void ImageTransfer::Pimpl::initTcpClient(const addrinfo* remoteAddressInfo, const addrinfo* localAddressInfo) {
320  if(remoteAddressInfo == nullptr) {
321  throw std::runtime_error("No remote address specified");
322  }
323 
324  protocol.reset(new ImageProtocol(ImageProtocol::PROTOCOL_TCP));
325 
326  // Connect
327  socket = ::socket(remoteAddressInfo->ai_family, remoteAddressInfo->ai_socktype,
328  remoteAddressInfo->ai_protocol);
329  if(socket == INVALID_SOCKET) {
330  TransferException ex("Error creating socket: " + string(strerror(errno)));
331  throw ex;
332  }
333 
334  if(localAddressInfo != nullptr) {
335  // Enable reuse address
336  int enable = 1;
337  setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
338 
339  // Bind to local port
340  if (::bind(socket, localAddressInfo->ai_addr, static_cast<int>(localAddressInfo->ai_addrlen)) < 0) {
341  TransferException ex("Error binding socket: " + string(strerror(errno)));
342  throw ex;
343  }
344  }
345 
346  // Perform connection
347  if(connect(socket, remoteAddressInfo->ai_addr, static_cast<int>(remoteAddressInfo->ai_addrlen)) < 0) {
348  TransferException ex("Error connection to destination address: " + string(strerror(errno)));
349  throw ex;
350  }
351 
352  // Set special socket options
353  setSocketOptions();
354 }
355 
356 void ImageTransfer::Pimpl::initTcpServer(const addrinfo* localAddressInfo) {
357  if(localAddressInfo == nullptr) {
358  throw std::runtime_error("No local address specified");
359  }
360 
361  protocol.reset(new ImageProtocol(ImageProtocol::PROTOCOL_TCP));
362 
363  // Create socket
364  serverSocket = ::socket(localAddressInfo->ai_family, localAddressInfo->ai_socktype,
365  localAddressInfo->ai_protocol);
366  if (serverSocket == INVALID_SOCKET) {
367  TransferException ex("Error opening socket: " + string(strerror(errno)));
368  throw ex;
369  }
370 
371  // Enable reuse address
372  int enable = 1;
373  setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
374 
375  // Open a server port
376  if (::bind(serverSocket, localAddressInfo->ai_addr, static_cast<int>(localAddressInfo->ai_addrlen)) < 0) {
377  TransferException ex("Error binding socket: " + string(strerror(errno)));
378  throw ex;
379  }
380 
381  // Make the server socket non-blocking
382 #ifdef _WIN32
383  unsigned long on = 1;
384  ioctlsocket(serverSocket, FIONBIO, &on);
385 #else
386  fcntl(serverSocket, F_SETFL, O_NONBLOCK);
387 #endif
388 
389  // Listen on port
390  listen(serverSocket, 1);
391 }
392 
393 void ImageTransfer::Pimpl::initUdp(const addrinfo* remoteAddressInfo, const addrinfo* localAddressInfo) {
394  if(localAddressInfo == nullptr) {
395  throw std::runtime_error("No local address specified");
396  }
397 
398  protocol.reset(new ImageProtocol(ImageProtocol::PROTOCOL_UDP, maxUdpPacketSize));
399  // Create socket
400  socket = ::socket(localAddressInfo->ai_family, localAddressInfo->ai_socktype,
401  localAddressInfo->ai_protocol);
402  if(socket == INVALID_SOCKET) {
403  TransferException ex("Error creating socket: " + string(strerror(errno)));
404  throw ex;
405  }
406 
407  // Enable reuse address
408  int enable = 1;
409  setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable), sizeof(int));
410 
411  // Bind socket to port
412  if (::bind(socket, localAddressInfo->ai_addr, static_cast<int>(localAddressInfo->ai_addrlen)) < 0) {
413  TransferException ex("Error binding socket: " + string(strerror(errno)));
414  throw ex;
415  }
416 
417  if(remoteAddressInfo != nullptr) {
418  // Store remote address
419  if(remoteAddressInfo->ai_addrlen != sizeof(udpAddress)) {
420  throw TransferException("Illegal address length");
421  }
422  memcpy(&udpAddress, remoteAddressInfo->ai_addr, sizeof(udpAddress));
423  }
424 
425  // Set special socket options
426  setSocketOptions();
427 }
428 
429 bool ImageTransfer::Pimpl::tryAccept() {
430  if(mode != TCP_SERVER) {
431  throw TransferException("Connections can only be accepted in tcp server mode");
432  }
433 
434  // Accept one connection
435  socklen_t clientAddressLength = sizeof(clientAddress);
436 
437  SOCKET newSocket = accept(serverSocket,
438  reinterpret_cast<sockaddr *>(&clientAddress),
439  &clientAddressLength);
440 
441  if(newSocket == INVALID_SOCKET) {
442  if(errno == EWOULDBLOCK || errno == ETIMEDOUT) {
443  // No connection
444  return false;
445  } else {
446  TransferException ex("Error accepting connection: " + string(strerror(errno)));
447  throw ex;
448  }
449  }
450 
451  // Close old socket and set new socket
452  if(socket != INVALID_SOCKET) {
453  close(socket);
454  }
455  socket = newSocket;
456 
457  // Set special socket options
458  setSocketOptions();
459 
460  // Reset connection data
461  protocol->resetTransfer();
462  protocol->resetReception();
463 
464  return true;
465 }
466 
467 std::string ImageTransfer::Pimpl::getClientAddress() const {
468  if(socket == INVALID_SOCKET) {
469  return ""; // No client connected
470  }
471 
472  char strPort[11];
473  snprintf(strPort, sizeof(strPort), ":%d", clientAddress.sin_port);
474 
475  return string(inet_ntoa(clientAddress.sin_addr)) + strPort;
476 }
477 
478 void ImageTransfer::Pimpl::setSocketOptions() {
479  if(mode == TCP_SERVER) {
480  // Make sure the client socket didn't inherit the non-blocking mode from the server socket
481 #ifdef _WIN32
482  unsigned long on = 0;
483  ioctlsocket(socket, FIONBIO, &on);
484 #else
485  fcntl(socket, F_SETFL, 0);
486 #endif
487  }
488 
489  // Set the socket buffer sizes
490  if(bufferSize > 0) {
491  setsockopt(socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
492  setsockopt(socket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
493  }
494 
495  // Set sending and receive timeouts
496 #ifdef _WIN32
497  unsigned int timeout = 1000;
498 #else
499  struct timeval timeout;
500  timeout.tv_sec = 1;
501  timeout.tv_usec = 0;
502 #endif
503 
504  setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
505  setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), sizeof(timeout));
506 
507  // Disable multicast loops for improved performance
508  unsigned char loop = 0;
509  setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char*>(&loop), sizeof(loop));
510 
511  // Try to set a more suitable congestion control algorithm for TCP streams
512 #ifdef TCP_CONGESTION
513  if(mode == TCP_SERVER || mode == TCP_CLIENT) {
514  char optval[16];
515  strcpy(optval, "westwood");
516  if (setsockopt(socket, IPPROTO_TCP, TCP_CONGESTION, optval, strlen(optval)) < 0) {
517  // Can't set westwood. Let's try reno
518  strcpy(optval, "reno");
519  setsockopt(socket, IPPROTO_TCP, TCP_CONGESTION, optval, strlen(optval));
520  }
521  }
522 #endif
523 }
524 
525 void ImageTransfer::Pimpl::setRawTransferData(const ImagePair& metaData,
526  unsigned char* rawData, int secondTileWidth, int validBytes) {
527  protocol->setRawTransferData(metaData, rawData, secondTileWidth, validBytes);
528  currentMsg = nullptr;
529 }
530 
531 void ImageTransfer::Pimpl::setRawValidBytes(int validBytes) {
532  protocol->setRawValidBytes(validBytes);
533 }
534 
535 void ImageTransfer::Pimpl::setTransferImagePair(const ImagePair& imagePair) {
536  protocol->setTransferImagePair(imagePair);
537  currentMsg = nullptr;
538 }
539 
540 void ImageTransfer::Pimpl::win32SetBlocking(bool block) {
541 #ifdef _WIN32
542  if(block != socketIsBlocking) {
543  // Windows doesn't support MSG_DONTWAIT. Have to touch the socket
544  unsigned long on = block ? 0 : 1;
545  ioctlsocket(socket, FIONBIO, &on);
546 
547  socketIsBlocking = block;
548  }
549 #endif
550 }
551 
552 ImageTransfer::TransferStatus ImageTransfer::Pimpl::transferData(bool block) {
553  if(currentMsg == nullptr) {
554  currentMsgOffset = 0;
555  currentMsg = protocol->getTransferMessage(currentMsgLen);
556 
557  if(currentMsg == nullptr) {
558  return NO_VALID_DATA;
559  }
560  }
561 
562  while(currentMsg != nullptr) {
563  int writing = (int)(currentMsgLen - currentMsgOffset);
564  int written = 0;
565 
566  win32SetBlocking(block);
567 
568  switch(mode) {
569  case TCP_SERVER:
570  case TCP_CLIENT:
571  written = send(socket, reinterpret_cast<const char*>(&currentMsg[currentMsgOffset]), writing,
572  block ? 0 : MSG_DONTWAIT);
573  break;
574  case UDP:
575  written = sendto(socket, reinterpret_cast<const char*>(&currentMsg[currentMsgOffset]), writing,
576  block ? 0 : MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&udpAddress), sizeof(udpAddress));
577  break;
578  }
579 
580  unsigned long sendError = errno;
581 
582  if(written < 0) {
583  if(!block && (sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT)) {
584  // The socket is not yet ready for a new transfer
585  return WOULD_BLOCK;
586  }
587  else if(sendError == ECONNRESET && (mode == TCP_SERVER || mode == TCP_CLIENT)) {
588  // Connection closed by remote host
589  close(socket);
590  socket = INVALID_SOCKET;
591  return CONNECTION_CLOSED;
592  } else {
593  TransferException ex("Error sending message: " + string(strerror(sendError)));
594  throw ex;
595  }
596  } else if(written != writing) {
597  // The message has been transmitted partially
598  if(mode == UDP) {
599  throw TransferException("Unable to transmit complete UDP message");
600  }
601 
602  currentMsgOffset+= written;
603  if(!block) {
604  return PARTIAL_TRANSFER;
605  }
606  } else if(!block) {
607  // Only one iteration allowed in non-blocking mode
608  currentMsg = nullptr;
609  break;
610  } else {
611  // Get next message
612  currentMsg = protocol->getTransferMessage(currentMsgLen);
613  currentMsgOffset = 0;
614  }
615  }
616 
617  if(mode == TCP_SERVER && protocol->transferComplete()) {
618  // Force a flush by turning the nagle algorithm off and on
619  int flag = 1;
620  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
621  flag = 0;
622  setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
623  }
624 
625  if(protocol->transferComplete()) {
626  return ALL_TRANSFERRED;
627  } else {
628  return PARTIAL_TRANSFER;
629  }
630 }
631 
632 bool ImageTransfer::Pimpl::receiveImagePair(ImagePair& imagePair, bool block) {
633  int validRows = 0;
634  bool complete = false;
635 
636  while(!complete) {
637  if(!receivePartialImagePair(imagePair, validRows, complete, block)) {
638  return false;
639  }
640  }
641 
642  return true;
643 }
644 
645 bool ImageTransfer::Pimpl::receivePartialImagePair(ImagePair& imagePair,
646  int& validRows, bool& complete, bool block) {
647  if(receptionFailed) {
648  // Notify about reception errors by returning false once
649  receptionFailed = false;
650  return false;
651  }
652 
653  // Try to receive further image data
654  while(!protocol->imagesReceived() && receiveNetworkData(block)) {
655  block = false;
656  }
657 
658  // Get received image
659  return protocol->getPartiallyReceivedImagePair(imagePair, validRows, complete);
660 }
661 
662 int ImageTransfer::Pimpl::receiveSingleNetworkMessages(bool block) {
663  int maxLength = 0;
664  char* buffer = reinterpret_cast<char*>(protocol->getNextReceiveBuffer(maxLength));
665 
666  int bytesReceived = recv(socket, buffer, maxLength,
667 #ifdef _WIN32
668  0
669 #else
670  block ? 0 : MSG_DONTWAIT
671 #endif
672  );
673 
674  if(bytesReceived > 0) {
675  if(!protocol->processReceivedMessage(bytesReceived)) {
676  if(mode == TCP_CLIENT) {
677  receptionFailed = true;
678  }
679  }
680  }
681 
682  return bytesReceived;
683 }
684 
685 bool ImageTransfer::Pimpl::receiveNetworkData(bool block) {
686  win32SetBlocking(block);
687 
688  int received = receiveSingleNetworkMessages(block);
689  bool ret = true;
690 
691  if(received == 0 && (mode == TCP_SERVER || mode == TCP_CLIENT)) {
692  // Connection closed by remote host
693  close(socket);
694  socket = INVALID_SOCKET;
695  ret = false;
696  } else if(received < 0) {
697  if(errno == EWOULDBLOCK || errno == EINTR || errno == ETIMEDOUT || errno == WSA_IO_PENDING) {
698  // Reception was cancelled because it took too long,
699  // or because of some signal. We reset reception for the current frame.
700  ret = false;
701  } else {
702  TransferException ex("Error reading from socket: " + string(strerror(errno)));
703  throw ex;
704  }
705  }
706 
707  return ret;
708 }
709 
710 void ImageTransfer::Pimpl::disconnect() {
711  if(mode != TCP_SERVER && mode != TCP_CLIENT) {
712  throw TransferException("Only TCP transfers can be disconnected");
713  }
714  if(socket != INVALID_SOCKET) {
715  close(socket);
716  socket = INVALID_SOCKET;
717  }
718 }
719 
720 bool ImageTransfer::Pimpl::isClientConnected() const {
721  return socket != INVALID_SOCKET;
722 }
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