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