23 #include "visiontransfer/imagetransfer.h" 24 #include "visiontransfer/exceptions.h" 25 #include "visiontransfer/datablockprotocol.h" 26 #include "visiontransfer/networking.h" 32 class ImageTransfer::Pimpl {
35 bool server,
int bufferSize,
int maxUdpPacketSize);
39 void setRawTransferData(
const ImagePair& metaData,
unsigned char* rawData,
40 int secondTileWidth = 0,
int validBytes = 0x7FFFFFFF);
41 void setRawValidBytes(
int validBytes);
42 void setTransferImagePair(
const ImagePair& imagePair);
43 TransferStatus transferData();
44 bool receiveImagePair(
ImagePair& imagePair);
45 bool receivePartialImagePair(
ImagePair& imagePair,
int& validRows,
bool& complete);
46 int getNumDroppedFrames()
const;
47 bool isConnected()
const;
49 std::string getRemoteAddress()
const;
60 std::recursive_mutex receiveMutex;
61 std::recursive_mutex sendMutex;
65 SOCKET tcpServerSocket;
66 sockaddr_in remoteAddress;
69 std::unique_ptr<ImageProtocol> protocol;
74 const unsigned char* currentMsg;
77 void setSocketOptions();
78 void setSocketBlocking(
bool blocking, SOCKET socket);
81 void initTcpServer(
const addrinfo* addressInfo);
82 void initTcpClient(
const addrinfo* addressInfo);
83 void initUdp(
const addrinfo* addressInfo);
86 bool receiveNetworkData(
bool block);
89 bool sendNetworkMessage(
const unsigned char* msg,
int length);
90 void sendPendingControlMessages();
92 bool selectSocket(
bool read,
bool wait);
93 void closeSocket(SOCKET& socket);
100 pimpl(new Pimpl(address, service, protType, server, bufferSize, maxUdpPacketSize)) {
105 pimpl(new Pimpl(device.getIpAddress().c_str(),
"7681", static_cast<
ImageProtocol::ProtocolType>(device.getNetworkProtocol()),
106 false, bufferSize, maxUdpPacketSize)) {
110 ImageTransfer::~ImageTransfer() {
115 int secondTileWidth,
int validBytes) {
116 pimpl->setRawTransferData(metaData, rawData, secondTileWidth, validBytes);
120 pimpl->setRawValidBytes(validBytes);
124 pimpl->setTransferImagePair(imagePair);
128 return pimpl->transferData();
132 return pimpl->receiveImagePair(imagePair);
136 return pimpl->receivePartialImagePair(imagePair, validRows, complete);
140 return pimpl->getNumDroppedFrames();
144 return pimpl->isConnected();
152 return pimpl->getRemoteAddress();
156 return pimpl->tryAccept();
160 ImageTransfer::Pimpl::Pimpl(
const char* address,
const char* service,
162 bufferSize,
int maxUdpPacketSize)
163 : protType(protType), isServer(server), bufferSize(bufferSize),
164 maxUdpPacketSize(maxUdpPacketSize),
165 clientSocket(INVALID_SOCKET), tcpServerSocket(INVALID_SOCKET),
166 currentMsgLen(0), currentMsgOffset(0), currentMsg(
nullptr) {
171 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
176 signal(SIGPIPE, SIG_IGN);
179 memset(&remoteAddress, 0,
sizeof(remoteAddress));
182 if(address ==
nullptr ||
string(address) ==
"") {
188 memset(&hints, 0,
sizeof(hints));
189 hints.ai_family = AF_INET;
192 hints.ai_protocol = 0;
194 addrinfo* addressInfo =
nullptr;
195 if(service !=
nullptr &&
string(service) !=
"") {
196 if(getaddrinfo(address, service, &hints, &addressInfo) != 0 || addressInfo ==
nullptr) {
202 if(addressInfo->ai_addrlen !=
sizeof(remoteAddress)) {
208 initUdp(addressInfo);
210 initTcpServer(addressInfo);
212 initTcpClient(addressInfo);
215 freeaddrinfo(addressInfo);
219 if(addressInfo !=
nullptr) {
220 freeaddrinfo(addressInfo);
224 ImageTransfer::Pimpl::~Pimpl() {
225 if(clientSocket != INVALID_SOCKET) {
226 closeSocket(clientSocket);
228 if(tcpServerSocket != INVALID_SOCKET) {
229 closeSocket(tcpServerSocket);
233 void ImageTransfer::Pimpl::initTcpClient(
const addrinfo* addressInfo) {
237 clientSocket = ::socket(addressInfo->ai_family, addressInfo->ai_socktype,
238 addressInfo->ai_protocol);
239 if(clientSocket == INVALID_SOCKET) {
245 if(connect(clientSocket, addressInfo->ai_addr, static_cast<int>(addressInfo->ai_addrlen)) < 0) {
246 TransferException ex(
"Error connection to destination address: " +
string(strerror(errno)));
250 memcpy(&remoteAddress, addressInfo->ai_addr,
sizeof(remoteAddress));
256 void ImageTransfer::Pimpl::initTcpServer(
const addrinfo* addressInfo) {
260 tcpServerSocket = ::socket(addressInfo->ai_family, addressInfo->ai_socktype,
261 addressInfo->ai_protocol);
262 if (tcpServerSocket == INVALID_SOCKET) {
269 setsockopt(tcpServerSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable),
sizeof(
int));
272 if (::bind(tcpServerSocket, addressInfo->ai_addr, static_cast<int>(addressInfo->ai_addrlen)) < 0) {
277 clientSocket = INVALID_SOCKET;
280 setSocketBlocking(
false, tcpServerSocket);
283 listen(tcpServerSocket, 1);
286 void ImageTransfer::Pimpl::initUdp(
const addrinfo* addressInfo) {
289 clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
290 if(clientSocket == INVALID_SOCKET) {
291 TransferException ex(
"Error creating receive socket: " +
string(strerror(errno)));
297 setsockopt(clientSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enable),
sizeof(
int));
300 if(isServer && addressInfo !=
nullptr) {
301 if (::bind(clientSocket, addressInfo->ai_addr, static_cast<int>(addressInfo->ai_addrlen)) < 0) {
308 memcpy(&remoteAddress, addressInfo->ai_addr,
sizeof(remoteAddress));
315 bool ImageTransfer::Pimpl::tryAccept() {
320 unique_lock<recursive_mutex> recvLock(receiveMutex);
321 unique_lock<recursive_mutex> sendLock(sendMutex);
324 socklen_t clientAddressLength =
sizeof(remoteAddress);
326 SOCKET newSocket = accept(tcpServerSocket,
327 reinterpret_cast<sockaddr *>(&remoteAddress),
328 &clientAddressLength);
330 if(clientAddressLength !=
sizeof(remoteAddress)) {
334 if(newSocket == INVALID_SOCKET) {
335 if(errno == EWOULDBLOCK || errno == ETIMEDOUT) {
345 if(clientSocket != INVALID_SOCKET) {
346 closeSocket(clientSocket);
348 clientSocket = newSocket;
354 protocol->resetTransfer();
355 protocol->resetReception();
356 currentMsg =
nullptr;
361 std::string ImageTransfer::Pimpl::getRemoteAddress()
const {
362 unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex));
364 if(remoteAddress.sin_family != AF_INET) {
369 snprintf(strPort,
sizeof(strPort),
":%d", remoteAddress.sin_port);
371 return string(inet_ntoa(remoteAddress.sin_addr)) + strPort;
374 void ImageTransfer::Pimpl::setSocketOptions() {
377 setsockopt(clientSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&bufferSize),
sizeof(bufferSize));
378 setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&bufferSize),
sizeof(bufferSize));
383 unsigned int timeout = 500;
385 struct timeval timeout;
387 timeout.tv_usec = 500000;
390 setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&timeout),
sizeof(timeout));
391 setsockopt(clientSocket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout),
sizeof(timeout));
392 setSocketBlocking(
true, clientSocket);
395 void ImageTransfer::Pimpl::setSocketBlocking(
bool blocking, SOCKET socket) {
397 unsigned long on = (blocking ? 0 : 1);
398 ioctlsocket(socket, FIONBIO, &on);
400 int flags = fcntl(socket, F_GETFL, 0);
403 flags &= ~O_NONBLOCK;
407 fcntl(socket, F_SETFL, flags);
412 void ImageTransfer::Pimpl::setRawTransferData(
const ImagePair& metaData,
413 unsigned char* rawData,
int secondTileWidth,
int validBytes) {
414 unique_lock<recursive_mutex> sendLock(sendMutex);
415 protocol->setRawTransferData(metaData, rawData, secondTileWidth, validBytes);
416 currentMsg =
nullptr;
419 void ImageTransfer::Pimpl::setRawValidBytes(
int validBytes) {
420 unique_lock<recursive_mutex> sendLock(sendMutex);
421 protocol->setRawValidBytes(validBytes);
424 void ImageTransfer::Pimpl::setTransferImagePair(
const ImagePair& imagePair) {
425 unique_lock<recursive_mutex> sendLock(sendMutex);
426 protocol->setTransferImagePair(imagePair);
427 currentMsg =
nullptr;
431 unique_lock<recursive_mutex> lock(sendMutex);
435 receiveNetworkData(
false);
438 if(remoteAddress.sin_family != AF_INET || !protocol->isConnected()) {
443 if(currentMsg ==
nullptr) {
444 currentMsgOffset = 0;
445 currentMsg = protocol->getTransferMessage(currentMsgLen);
447 if(currentMsg ==
nullptr) {
448 if(protocol->transferComplete()) {
457 bool dataTransferred = (currentMsg !=
nullptr);
458 while(currentMsg !=
nullptr) {
459 int writing = (int)(currentMsgLen - currentMsgOffset);
461 if(sendNetworkMessage(¤tMsg[currentMsgOffset], writing)) {
463 currentMsgOffset = 0;
464 currentMsg = protocol->getTransferMessage(currentMsgLen);
473 setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (
char *) &flag,
sizeof(
int));
475 setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (
char *) &flag,
sizeof(
int));
480 receiveNetworkData(
false);
483 if(protocol->transferComplete()) {
490 bool ImageTransfer::Pimpl::receiveImagePair(
ImagePair& imagePair) {
492 bool complete =
false;
494 std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
500 unsigned int time =
static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::milliseconds>(
501 std::chrono::steady_clock::now() - startTime).count());
510 bool ImageTransfer::Pimpl::receivePartialImagePair(
ImagePair& imagePair,
511 int& validRows,
bool& complete) {
512 unique_lock<recursive_mutex> lock(receiveMutex);
516 while(!protocol->imagesReceived() && receiveNetworkData(block)) {
521 return protocol->getPartiallyReceivedImagePair(imagePair, validRows, complete);
524 bool ImageTransfer::Pimpl::receiveNetworkData(
bool block) {
525 unique_lock<recursive_mutex> lock = block ?
526 unique_lock<recursive_mutex>(receiveMutex) : unique_lock<recursive_mutex>(receiveMutex, std::try_to_lock);
528 if(clientSocket == INVALID_SOCKET) {
533 sendPendingControlMessages();
535 if(!lock.owns_lock()) {
541 if(!block && !selectSocket(
true,
false)) {
546 char* buffer =
reinterpret_cast<char*
>(protocol->getNextReceiveBuffer(maxLength));
549 sockaddr_in fromAddress;
550 socklen_t fromSize =
sizeof(fromAddress);
552 int bytesReceived = recvfrom(clientSocket, buffer, maxLength,
553 0, reinterpret_cast<sockaddr*>(&fromAddress), &fromSize);
558 }
else if(bytesReceived < 0 && errno != EWOULDBLOCK && errno != EINTR &&
559 errno != ETIMEDOUT && errno != WSA_IO_PENDING && errno != WSAECONNRESET) {
562 }
else if(bytesReceived > 0) {
563 protocol->processReceivedMessage(bytesReceived);
564 if(protocol->newClientConnected()) {
566 memcpy(&remoteAddress, &fromAddress,
sizeof(remoteAddress));
570 return bytesReceived > 0;
573 void ImageTransfer::Pimpl::disconnect() {
576 unique_lock<recursive_mutex> recvLock(receiveMutex);
577 unique_lock<recursive_mutex> sendLock(sendMutex);
580 closeSocket(clientSocket);
582 memset(&remoteAddress, 0,
sizeof(remoteAddress));
585 bool ImageTransfer::Pimpl::isConnected()
const {
586 unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex));
588 return remoteAddress.sin_family == AF_INET && protocol->isConnected();
591 bool ImageTransfer::Pimpl::sendNetworkMessage(
const unsigned char* msg,
int length) {
594 sockaddr_in destAddr;
597 unique_lock<recursive_mutex> lock(sendMutex);
598 destAddr = remoteAddress;
599 destSocket = clientSocket;
602 if(destAddr.sin_family != AF_INET) {
606 written = sendto(destSocket, reinterpret_cast<const char*>(msg), length, 0,
607 reinterpret_cast<sockaddr*>(&destAddr),
sizeof(destAddr));
611 unique_lock<recursive_mutex> lock(sendMutex);
612 destSocket = clientSocket;
614 written = send(destSocket, reinterpret_cast<const char*>(msg), length, 0);
617 unsigned long sendError = errno;
620 if(sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT) {
623 }
else if(sendError == EPIPE) {
628 TransferException ex(
"Error sending network packet: " +
string(strerror(errno)));
631 }
else if(written != length) {
637 currentMsgOffset += written;
645 void ImageTransfer::Pimpl::sendPendingControlMessages() {
646 const unsigned char* controlMsgData =
nullptr;
647 int controlMsgLen = 0;
650 unique_lock<recursive_mutex> lock(sendMutex);
651 if(remoteAddress.sin_family != AF_INET) {
655 controlMsgData = protocol->getNextControlMessage(controlMsgLen);
657 if(controlMsgData !=
nullptr) {
658 sendNetworkMessage(controlMsgData, controlMsgLen);
665 int ImageTransfer::Pimpl::getNumDroppedFrames()
const {
666 return protocol->getNumDroppedFrames();
669 bool ImageTransfer::Pimpl::selectSocket(
bool read,
bool wait) {
672 unique_lock<recursive_mutex> lock(sendMutex);
687 if(select(sock+1, (read ? &fds :
nullptr), (!read ? &fds :
nullptr),
nullptr, &tv) <= 0) {
695 void ImageTransfer::Pimpl::closeSocket(SOCKET& socket) {
696 shutdown(socket, SHUT_WR);
700 for(
int i=0; i<3; i++) {
701 int received = recv(clientSocket, buffer,
sizeof(buffer), 0);
708 socket = INVALID_SOCKET;
No network connection has been established.
A lightweight protocol for transferring image pairs.
bool receivePartialImagePair(ImagePair &imagePair, int &validRows, bool &complete)
Returns the received image pair, even if it is not yet complete.
std::string getRemoteAddress() const
Returns the address of the remote host.
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.
bool isConnected() const
Returns true if a remote connection is established.
Exception class that is used for all transfer exceptions.
The image pair has been transferred completely.
ProtocolType
Supported network protocols.
TransferStatus
The result of a partial image transfer.
A set of two images, which are usually the left camera image and the disparity map.
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.
ImageTransfer(const char *address, const char *service="7681", ImageProtocol::ProtocolType protType=ImageProtocol::PROTOCOL_UDP, bool server=false, int bufferSize=1048576, int maxUdpPacketSize=1472)
Creates a new transfer object by manually specifying the target address.
Aggregates information about a discovered device.
int getNumDroppedFrames() const
Returns the number of frames that have been dropped since connecting to the current remote host...
bool receiveImagePair(ImagePair &imagePair)
Waits for and receives a new image pair.
There is currently no more data that could be transmitted.
The connection oriented TCP transport protocol.
TransferStatus transferData()
Performs a partial (or full) image transmission.