22 #include "visiontransfer/imagetransfer.h" 23 #include "visiontransfer/exceptions.h" 24 #include "visiontransfer/datablockprotocol.h" 29 #define snprintf _snprintf_s 35 #define _WIN32_WINNT 0x501 37 #define _WINSOCK_DEPRECATED_NO_WARNINGS 58 #define EWOULDBLOCK WSAEWOULDBLOCK 59 #define ECONNRESET WSAECONNRESET 60 #define ETIMEDOUT WSAETIMEDOUT 61 #define MSG_DONTWAIT 0 63 #define close closesocket 68 #define errno WSAGetLastError() 69 #define strerror win_strerror 71 std::string win_strerror(
unsigned long error) {
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";
80 snprintf(buffer,
sizeof(buffer),
"%s (%lu)", str, error);
82 return std::string(buffer);
87 #include <arpa/inet.h> 88 #include <netinet/tcp.h> 89 #include <sys/types.h> 90 #include <sys/socket.h> 92 #include <netinet/in.h> 99 #define INVALID_SOCKET -1 102 #define WSA_IO_PENDING 0 109 class ImageTransfer::Pimpl {
111 Pimpl(OperationMode mode,
const char* remoteAddress,
const char* remoteService,
112 const char* localAddress,
const char* localService,
int bufferSize,
113 int maxUdpPacketSize);
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);
125 bool isClientConnected()
const;
127 std::string getClientAddress()
const;
130 static constexpr
int UDP_BUFFERS = 256;
131 static constexpr
int TCP_BUFFER_SIZE = 0xFFFF;
137 int maxUdpPacketSize;
146 sockaddr_in clientAddress;
149 sockaddr_in udpAddress;
152 std::unique_ptr<ImageProtocol> protocol;
156 int currentMsgOffset;
157 const unsigned char* currentMsg;
163 bool receptionFailed;
165 bool socketIsBlocking;
168 void setSocketOptions();
171 void initTcpClient(
const addrinfo& remoteAddressInfo,
const addrinfo& localAddressInfo);
172 void initTcpServer(
const addrinfo& localAddressInfo);
173 void initUdp(
const addrinfo& remoteAddressInfo,
const addrinfo& localAddressInfo);
176 int receiveSingleNetworkMessages(
bool block);
177 bool receiveNetworkData(
bool block);
179 void win32SetBlocking(
bool block);
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,
192 ImageTransfer::~ImageTransfer() {
197 int secondTileWidth,
int validBytes) {
198 pimpl->setRawTransferData(metaData, rawData, secondTileWidth, validBytes);
202 pimpl->setRawValidBytes(validBytes);
206 pimpl->setTransferImagePair(imagePair);
210 return pimpl->transferData(block);
214 return pimpl->receiveImagePair(imagePair, block);
218 return pimpl->receivePartialImagePair(imagePair, validRows, complete, block);
222 return pimpl->tryAccept();
226 return pimpl->isClientConnected();
234 return pimpl->getClientAddress();
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) {
247 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
252 signal(SIGPIPE, SIG_IGN);
256 if(remoteAddress ==
nullptr ||
string(remoteAddress) ==
"") {
257 remoteAddress =
"0.0.0.0";
259 if(localAddress ==
nullptr ||
string(localAddress) ==
"") {
260 localAddress =
"0.0.0.0";
265 memset(&hints, 0,
sizeof(hints));
266 hints.ai_family = AF_INET;
269 hints.ai_protocol = 0;
271 addrinfo* remoteAddressInfo =
nullptr;
272 if(getaddrinfo(remoteAddress, remoteService, &hints, &remoteAddressInfo) != 0 || remoteAddressInfo ==
nullptr) {
273 TransferException ex(
"Error resolving remote address: " +
string(strerror(errno)));
277 addrinfo* localAddressInfo =
nullptr;
278 if(getaddrinfo(localAddress, localService, &hints, &localAddressInfo) != 0 || localAddressInfo ==
nullptr) {
279 TransferException ex(
"Error resolving local address: " +
string(strerror(errno)));
286 case TCP_CLIENT: initTcpClient(*remoteAddressInfo, *localAddressInfo);
break;
287 case TCP_SERVER: initTcpServer(*localAddressInfo);
break;
288 case UDP: initUdp(*remoteAddressInfo, *localAddressInfo);
break;
292 freeaddrinfo(remoteAddressInfo);
293 freeaddrinfo(localAddressInfo);
297 freeaddrinfo(remoteAddressInfo);
298 freeaddrinfo(localAddressInfo);
301 ImageTransfer::Pimpl::~Pimpl() {
302 if(socket != INVALID_SOCKET) {
306 if(serverSocket != INVALID_SOCKET) {
311 void ImageTransfer::Pimpl::initTcpClient(
const addrinfo& remoteAddressInfo,
const addrinfo& localAddressInfo) {
315 socket = ::socket(remoteAddressInfo.ai_family, remoteAddressInfo.ai_socktype,
316 remoteAddressInfo.ai_protocol);
317 if(socket == INVALID_SOCKET) {
324 setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable),
sizeof(
int));
327 if (::bind(socket, localAddressInfo.ai_addr, static_cast<int>(localAddressInfo.ai_addrlen)) < 0) {
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)));
342 void ImageTransfer::Pimpl::initTcpServer(
const addrinfo& localAddressInfo) {
346 serverSocket = ::socket(localAddressInfo.ai_family, localAddressInfo.ai_socktype,
347 localAddressInfo.ai_protocol);
348 if (serverSocket == INVALID_SOCKET) {
355 setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable),
sizeof(
int));
358 if (::bind(serverSocket, localAddressInfo.ai_addr, static_cast<int>(localAddressInfo.ai_addrlen)) < 0) {
365 unsigned long on = 1;
366 ioctlsocket(serverSocket, FIONBIO, &on);
368 fcntl(serverSocket, F_SETFL, O_NONBLOCK);
372 listen(serverSocket, 1);
375 void ImageTransfer::Pimpl::initUdp(
const addrinfo& remoteAddressInfo,
const addrinfo& localAddressInfo) {
378 socket = ::socket(localAddressInfo.ai_family, localAddressInfo.ai_socktype,
379 localAddressInfo.ai_protocol);
380 if(socket == INVALID_SOCKET) {
387 setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable),
sizeof(
int));
390 if (::bind(socket, localAddressInfo.ai_addr, static_cast<int>(localAddressInfo.ai_addrlen)) < 0) {
396 if(remoteAddressInfo.ai_addrlen !=
sizeof(udpAddress)) {
399 memcpy(&udpAddress, remoteAddressInfo.ai_addr,
sizeof(udpAddress));
405 bool ImageTransfer::Pimpl::tryAccept() {
411 socklen_t clientAddressLength =
sizeof(clientAddress);
413 SOCKET newSocket = accept(serverSocket,
414 reinterpret_cast<sockaddr *>(&clientAddress),
415 &clientAddressLength);
417 if(newSocket == INVALID_SOCKET) {
418 if(errno == EWOULDBLOCK || errno == ETIMEDOUT) {
428 if(socket != INVALID_SOCKET) {
437 protocol->resetTransfer();
438 protocol->resetReception();
443 std::string ImageTransfer::Pimpl::getClientAddress()
const {
444 if(socket == INVALID_SOCKET) {
449 snprintf(strPort,
sizeof(strPort),
":%d", clientAddress.sin_port);
451 return string(inet_ntoa(clientAddress.sin_addr)) + strPort;
454 void ImageTransfer::Pimpl::setSocketOptions() {
458 unsigned long on = 0;
459 ioctlsocket(socket, FIONBIO, &on);
461 fcntl(socket, F_SETFL, 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));
473 unsigned int timeout = 1000;
475 struct timeval timeout;
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));
484 unsigned char loop = 0;
485 setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char*>(&loop),
sizeof(loop));
488 #ifdef TCP_CONGESTION 491 strcpy(optval,
"westwood");
492 if (setsockopt(socket, IPPROTO_TCP, TCP_CONGESTION, optval, strlen(optval)) < 0) {
494 strcpy(optval,
"reno");
495 setsockopt(socket, IPPROTO_TCP, TCP_CONGESTION, optval, strlen(optval));
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;
507 void ImageTransfer::Pimpl::setRawValidBytes(
int validBytes) {
508 protocol->setRawValidBytes(validBytes);
511 void ImageTransfer::Pimpl::setTransferImagePair(
const ImagePair& imagePair) {
512 protocol->setTransferImagePair(imagePair);
513 currentMsg =
nullptr;
516 void ImageTransfer::Pimpl::win32SetBlocking(
bool block) {
518 if(block != socketIsBlocking) {
520 unsigned long on = block ? 0 : 1;
521 ioctlsocket(socket, FIONBIO, &on);
523 socketIsBlocking = block;
529 if(currentMsg ==
nullptr) {
530 currentMsgOffset = 0;
531 currentMsg = protocol->getTransferMessage(currentMsgLen);
533 if(currentMsg ==
nullptr) {
538 while(currentMsg !=
nullptr) {
539 int writing = (int)(currentMsgLen - currentMsgOffset);
542 win32SetBlocking(block);
547 written = send(socket, reinterpret_cast<const char*>(¤tMsg[currentMsgOffset]), writing,
548 block ? 0 : MSG_DONTWAIT);
551 written = sendto(socket, reinterpret_cast<const char*>(¤tMsg[currentMsgOffset]), writing,
552 block ? 0 : MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&udpAddress),
sizeof(udpAddress));
556 unsigned long sendError = errno;
559 if(!block && (sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT)) {
566 socket = INVALID_SOCKET;
572 }
else if(written != writing) {
578 currentMsgOffset+= written;
584 currentMsg =
nullptr;
588 currentMsg = protocol->getTransferMessage(currentMsgLen);
589 currentMsgOffset = 0;
593 if(mode ==
TCP_SERVER && protocol->transferComplete()) {
596 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (
char *) &flag,
sizeof(
int));
598 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (
char *) &flag,
sizeof(
int));
601 if(protocol->transferComplete()) {
608 bool ImageTransfer::Pimpl::receiveImagePair(
ImagePair& imagePair,
bool block) {
610 bool complete =
false;
621 bool ImageTransfer::Pimpl::receivePartialImagePair(
ImagePair& imagePair,
622 int& validRows,
bool& complete,
bool block) {
623 if(receptionFailed) {
625 receptionFailed =
false;
630 while(!protocol->imagesReceived() && receiveNetworkData(block)) {
635 return protocol->getPartiallyReceivedImagePair(imagePair, validRows, complete);
638 int ImageTransfer::Pimpl::receiveSingleNetworkMessages(
bool block) {
640 char* buffer =
reinterpret_cast<char*
>(protocol->getNextReceiveBuffer(maxLength));
642 int bytesReceived = recv(socket, buffer, maxLength,
646 block ? 0 : MSG_DONTWAIT
650 if(bytesReceived > 0) {
651 if(!protocol->processReceivedMessage(bytesReceived)) {
653 receptionFailed =
true;
658 return bytesReceived;
661 bool ImageTransfer::Pimpl::receiveNetworkData(
bool block) {
662 win32SetBlocking(block);
664 int received = receiveSingleNetworkMessages(block);
670 socket = INVALID_SOCKET;
672 }
else if(received < 0) {
673 if(errno == EWOULDBLOCK || errno == EINTR || errno == ETIMEDOUT || errno == WSA_IO_PENDING) {
686 void ImageTransfer::Pimpl::disconnect() {
690 if(socket != INVALID_SOCKET) {
692 socket = INVALID_SOCKET;
696 bool ImageTransfer::Pimpl::isClientConnected()
const {
697 return socket != INVALID_SOCKET;
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.
Using TCP and acting as communication server.
A lightweight protocol for transferring image pairs.
OperationMode
Supported transfer modes.
Using TCP and acting as communication client.
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.
Exception class that is used for all transfer exceptions.
The image pair has been transferred completely.
TransferStatus
The result of a partial image transfer.
A set of two images, which are usually the left camera image and the disparity map.
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.
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.
The connection oriented TCP transport protocol.