demo version
This commit is contained in:
parent
fbb282a801
commit
672d6daa8e
125 changed files with 17918 additions and 1481 deletions
|
@ -0,0 +1,642 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2021 Nerian Vision GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*******************************************************************************/
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include "visiontransfer/imagetransfer.h"
|
||||
#include "visiontransfer/exceptions.h"
|
||||
#include "visiontransfer/datablockprotocol.h"
|
||||
#include "visiontransfer/networking.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace visiontransfer;
|
||||
using namespace visiontransfer::internal;
|
||||
|
||||
namespace visiontransfer {
|
||||
|
||||
/*************** Pimpl class containing all private members ***********/
|
||||
|
||||
class ImageTransfer::Pimpl {
|
||||
public:
|
||||
Pimpl(const char* address, const char* service, ImageProtocol::ProtocolType protType,
|
||||
bool server, int bufferSize, int maxUdpPacketSize);
|
||||
~Pimpl();
|
||||
|
||||
// Redeclaration of public members
|
||||
void setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& rawData,
|
||||
int firstTileWidth = 0, int secondTileWidth = 0, int validBytes = 0x7FFFFFFF);
|
||||
void setRawValidBytes(const std::vector<int>& validBytes);
|
||||
void setTransferImageSet(const ImageSet& imageSet);
|
||||
TransferStatus transferData();
|
||||
bool receiveImageSet(ImageSet& imageSet);
|
||||
bool receivePartialImageSet(ImageSet& imageSet, int& validRows, bool& complete);
|
||||
int getNumDroppedFrames() const;
|
||||
bool isConnected() const;
|
||||
void disconnect();
|
||||
std::string getRemoteAddress() const;
|
||||
bool tryAccept();
|
||||
|
||||
std::string statusReport();
|
||||
private:
|
||||
// Configuration parameters
|
||||
ImageProtocol::ProtocolType protType;
|
||||
bool isServer;
|
||||
int bufferSize;
|
||||
int maxUdpPacketSize;
|
||||
|
||||
// Thread synchronization
|
||||
std::recursive_mutex receiveMutex;
|
||||
std::recursive_mutex sendMutex;
|
||||
|
||||
// Transfer related members
|
||||
SOCKET clientSocket;
|
||||
SOCKET tcpServerSocket;
|
||||
sockaddr_in remoteAddress;
|
||||
|
||||
// Object for encoding and decoding the network protocol
|
||||
std::unique_ptr<ImageProtocol> protocol;
|
||||
|
||||
// Outstanding network message that still has to be transferred
|
||||
int currentMsgLen;
|
||||
int currentMsgOffset;
|
||||
const unsigned char* currentMsg;
|
||||
|
||||
// Socket configuration
|
||||
void setSocketOptions();
|
||||
|
||||
// Network socket initialization
|
||||
void initTcpServer(const addrinfo* addressInfo);
|
||||
void initTcpClient(const addrinfo* addressInfo);
|
||||
void initUdp(const addrinfo* addressInfo);
|
||||
|
||||
// Data reception
|
||||
bool receiveNetworkData(bool block);
|
||||
|
||||
// Data transmission
|
||||
bool sendNetworkMessage(const unsigned char* msg, int length);
|
||||
void sendPendingControlMessages();
|
||||
|
||||
bool selectSocket(bool read, bool wait);
|
||||
};
|
||||
|
||||
/******************** Stubs for all public members ********************/
|
||||
|
||||
ImageTransfer::ImageTransfer(const char* address, const char* service,
|
||||
ImageProtocol::ProtocolType protType, bool server, int bufferSize, int maxUdpPacketSize):
|
||||
pimpl(new Pimpl(address, service, protType, server, bufferSize, maxUdpPacketSize)) {
|
||||
// All initialization in the pimpl class
|
||||
}
|
||||
|
||||
ImageTransfer::ImageTransfer(const DeviceInfo& device, int bufferSize, int maxUdpPacketSize):
|
||||
pimpl(new Pimpl(device.getIpAddress().c_str(), "7681", static_cast<ImageProtocol::ProtocolType>(device.getNetworkProtocol()),
|
||||
false, bufferSize, maxUdpPacketSize)) {
|
||||
// All initialization in the pimpl class
|
||||
}
|
||||
|
||||
ImageTransfer::~ImageTransfer() {
|
||||
delete pimpl;
|
||||
}
|
||||
|
||||
void ImageTransfer::setRawTransferData(const ImageSet& metaData, const std::vector<unsigned char*>& rawData,
|
||||
int firstTileWidth, int secondTileWidth, int validBytes) {
|
||||
pimpl->setRawTransferData(metaData, rawData, firstTileWidth, secondTileWidth, validBytes);
|
||||
}
|
||||
|
||||
void ImageTransfer::setRawValidBytes(const std::vector<int>& validBytes) {
|
||||
pimpl->setRawValidBytes(validBytes);
|
||||
}
|
||||
|
||||
void ImageTransfer::setTransferImageSet(const ImageSet& imageSet) {
|
||||
pimpl->setTransferImageSet(imageSet);
|
||||
}
|
||||
|
||||
ImageTransfer::TransferStatus ImageTransfer::transferData() {
|
||||
return pimpl->transferData();
|
||||
}
|
||||
|
||||
bool ImageTransfer::receiveImageSet(ImageSet& imageSet) {
|
||||
return pimpl->receiveImageSet(imageSet);
|
||||
}
|
||||
|
||||
bool ImageTransfer::receivePartialImageSet(ImageSet& imageSet, int& validRows, bool& complete) {
|
||||
return pimpl->receivePartialImageSet(imageSet, validRows, complete);
|
||||
}
|
||||
|
||||
int ImageTransfer::getNumDroppedFrames() const {
|
||||
return pimpl->getNumDroppedFrames();
|
||||
}
|
||||
|
||||
bool ImageTransfer::isConnected() const {
|
||||
return pimpl->isConnected();
|
||||
}
|
||||
|
||||
void ImageTransfer::disconnect() {
|
||||
pimpl->disconnect();
|
||||
}
|
||||
|
||||
std::string ImageTransfer::getRemoteAddress() const {
|
||||
return pimpl->getRemoteAddress();
|
||||
}
|
||||
|
||||
bool ImageTransfer::tryAccept() {
|
||||
return pimpl->tryAccept();
|
||||
}
|
||||
|
||||
/******************** Implementation in pimpl class *******************/
|
||||
ImageTransfer::Pimpl::Pimpl(const char* address, const char* service,
|
||||
ImageProtocol::ProtocolType protType, bool server, int
|
||||
bufferSize, int maxUdpPacketSize)
|
||||
: protType(protType), isServer(server), bufferSize(bufferSize),
|
||||
maxUdpPacketSize(maxUdpPacketSize),
|
||||
clientSocket(INVALID_SOCKET), tcpServerSocket(INVALID_SOCKET),
|
||||
currentMsgLen(0), currentMsgOffset(0), currentMsg(nullptr) {
|
||||
|
||||
Networking::initNetworking();
|
||||
#ifndef _WIN32
|
||||
// We don't want to be interrupted by the pipe signal
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
memset(&remoteAddress, 0, sizeof(remoteAddress));
|
||||
|
||||
// If address is null we use the any address
|
||||
if(address == nullptr || string(address) == "") {
|
||||
address = "0.0.0.0";
|
||||
}
|
||||
|
||||
addrinfo* addressInfo = Networking::resolveAddress(address, service);
|
||||
|
||||
try {
|
||||
if(protType == ImageProtocol::PROTOCOL_UDP) {
|
||||
initUdp(addressInfo);
|
||||
} else if(protType == ImageProtocol::PROTOCOL_TCP && isServer) {
|
||||
initTcpServer(addressInfo);
|
||||
} else {
|
||||
initTcpClient(addressInfo);
|
||||
}
|
||||
} catch(...) {
|
||||
freeaddrinfo(addressInfo);
|
||||
throw;
|
||||
}
|
||||
|
||||
if(addressInfo != nullptr) {
|
||||
freeaddrinfo(addressInfo);
|
||||
}
|
||||
}
|
||||
|
||||
ImageTransfer::Pimpl::~Pimpl() {
|
||||
if(clientSocket != INVALID_SOCKET) {
|
||||
Networking::closeSocket(clientSocket);
|
||||
}
|
||||
if(tcpServerSocket != INVALID_SOCKET) {
|
||||
Networking::closeSocket(tcpServerSocket);
|
||||
}
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::initTcpClient(const addrinfo* addressInfo) {
|
||||
protocol.reset(new ImageProtocol(isServer, ImageProtocol::PROTOCOL_TCP));
|
||||
clientSocket = Networking::connectTcpSocket(addressInfo);
|
||||
memcpy(&remoteAddress, addressInfo->ai_addr, sizeof(remoteAddress));
|
||||
|
||||
// Set special socket options
|
||||
setSocketOptions();
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::initTcpServer(const addrinfo* addressInfo) {
|
||||
protocol.reset(new ImageProtocol(isServer, ImageProtocol::PROTOCOL_TCP));
|
||||
|
||||
// Create socket
|
||||
tcpServerSocket = ::socket(addressInfo->ai_family, addressInfo->ai_socktype,
|
||||
addressInfo->ai_protocol);
|
||||
if (tcpServerSocket == INVALID_SOCKET) {
|
||||
TransferException ex("Error opening socket: " + string(strerror(errno)));
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// Enable reuse address
|
||||
Networking::enableReuseAddress(tcpServerSocket, true);
|
||||
|
||||
// Open a server port
|
||||
Networking::bindSocket(tcpServerSocket, addressInfo);
|
||||
clientSocket = INVALID_SOCKET;
|
||||
|
||||
// Make the server socket non-blocking
|
||||
Networking::setSocketBlocking(tcpServerSocket, false);
|
||||
|
||||
// Listen on port
|
||||
listen(tcpServerSocket, 1);
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::initUdp(const addrinfo* addressInfo) {
|
||||
protocol.reset(new ImageProtocol(isServer, ImageProtocol::PROTOCOL_UDP, maxUdpPacketSize));
|
||||
// Create sockets
|
||||
clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if(clientSocket == INVALID_SOCKET) {
|
||||
TransferException ex("Error creating receive socket: " + string(strerror(errno)));
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// Enable reuse address
|
||||
Networking::enableReuseAddress(clientSocket, true);
|
||||
|
||||
// Bind socket to port
|
||||
if(isServer && addressInfo != nullptr) {
|
||||
Networking::bindSocket(clientSocket, addressInfo);
|
||||
}
|
||||
|
||||
if(!isServer) {
|
||||
memcpy(&remoteAddress, addressInfo->ai_addr, sizeof(remoteAddress));
|
||||
}
|
||||
|
||||
// Set special socket options
|
||||
setSocketOptions();
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::tryAccept() {
|
||||
if(protType != ImageProtocol::PROTOCOL_TCP || ! isServer) {
|
||||
throw TransferException("Connections can only be accepted in tcp server mode");
|
||||
}
|
||||
|
||||
unique_lock<recursive_mutex> recvLock(receiveMutex);
|
||||
unique_lock<recursive_mutex> sendLock(sendMutex);
|
||||
|
||||
// Accept one connection
|
||||
SOCKET newSocket = Networking::acceptConnection(tcpServerSocket, remoteAddress);
|
||||
if(newSocket == INVALID_SOCKET) {
|
||||
// No connection
|
||||
return false;
|
||||
}
|
||||
|
||||
if(clientSocket != INVALID_SOCKET) {
|
||||
Networking::closeSocket(clientSocket);
|
||||
}
|
||||
clientSocket = newSocket;
|
||||
|
||||
// Set special socket options
|
||||
setSocketOptions();
|
||||
|
||||
// Reset connection data
|
||||
protocol->resetTransfer();
|
||||
protocol->resetReception();
|
||||
currentMsg = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ImageTransfer::Pimpl::getRemoteAddress() const {
|
||||
unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); // either mutex will work
|
||||
|
||||
if(remoteAddress.sin_family != AF_INET) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char strPort[11];
|
||||
snprintf(strPort, sizeof(strPort), ":%d", remoteAddress.sin_port);
|
||||
|
||||
return string(inet_ntoa(remoteAddress.sin_addr)) + strPort;
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::setSocketOptions() {
|
||||
// Set the socket buffer sizes
|
||||
if(bufferSize > 0) {
|
||||
setsockopt(clientSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
|
||||
setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<char*>(&bufferSize), sizeof(bufferSize));
|
||||
}
|
||||
|
||||
Networking::setSocketTimeout(clientSocket, 500);
|
||||
Networking::setSocketBlocking(clientSocket, true);
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::setRawTransferData(const ImageSet& metaData,
|
||||
const std::vector<unsigned char*>& rawDataVec, int firstTileWidth, int secondTileWidth, int validBytes) {
|
||||
unique_lock<recursive_mutex> sendLock(sendMutex);
|
||||
protocol->setRawTransferData(metaData, rawDataVec, firstTileWidth, secondTileWidth, validBytes);
|
||||
currentMsg = nullptr;
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::setRawValidBytes(const std::vector<int>& validBytes) {
|
||||
unique_lock<recursive_mutex> sendLock(sendMutex);
|
||||
protocol->setRawValidBytes(validBytes);
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::setTransferImageSet(const ImageSet& imageSet) {
|
||||
unique_lock<recursive_mutex> sendLock(sendMutex);
|
||||
protocol->setTransferImageSet(imageSet);
|
||||
currentMsg = nullptr;
|
||||
}
|
||||
|
||||
ImageTransfer::TransferStatus ImageTransfer::Pimpl::transferData() {
|
||||
unique_lock<recursive_mutex> lock(sendMutex);
|
||||
|
||||
// First receive data in case a control message arrives
|
||||
if(protType == ImageProtocol::PROTOCOL_UDP) {
|
||||
receiveNetworkData(false);
|
||||
}
|
||||
|
||||
if(remoteAddress.sin_family != AF_INET || !protocol->isConnected()) {
|
||||
return NOT_CONNECTED;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
// Cork TCP to prevent sending of small packets
|
||||
if(protType == ImageProtocol::PROTOCOL_TCP) {
|
||||
int flag = 1;
|
||||
setsockopt(clientSocket, IPPROTO_TCP, TCP_CORK, (char *) &flag, sizeof(int));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get first message to transfer
|
||||
if(currentMsg == nullptr) {
|
||||
currentMsgOffset = 0;
|
||||
currentMsg = protocol->getTransferMessage(currentMsgLen);
|
||||
|
||||
if(currentMsg == nullptr) {
|
||||
if(protocol->transferComplete()) {
|
||||
return ALL_TRANSFERRED;
|
||||
} else {
|
||||
return NO_VALID_DATA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try transferring messages
|
||||
bool dataTransferred = (currentMsg != nullptr);
|
||||
while(currentMsg != nullptr) {
|
||||
int writing = (int)(currentMsgLen - currentMsgOffset);
|
||||
|
||||
if(sendNetworkMessage(¤tMsg[currentMsgOffset], writing)) {
|
||||
// Get next message
|
||||
currentMsgOffset = 0;
|
||||
currentMsg = protocol->getTransferMessage(currentMsgLen);
|
||||
} else {
|
||||
return WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
if(dataTransferred && protType == ImageProtocol::PROTOCOL_TCP && protocol->transferComplete()) {
|
||||
#ifndef _WIN32
|
||||
// Uncork - sends the assembled messages
|
||||
int flag = 0;
|
||||
setsockopt(clientSocket, IPPROTO_TCP, TCP_CORK, (char *) &flag, sizeof(int));
|
||||
#else
|
||||
// Force a flush for TCP by turning the nagle algorithm off and on
|
||||
int flag = 1;
|
||||
setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
|
||||
flag = 0;
|
||||
setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Also check for control messages at the end
|
||||
if(protType == ImageProtocol::PROTOCOL_UDP) {
|
||||
receiveNetworkData(false);
|
||||
}
|
||||
|
||||
if(protocol->transferComplete()) {
|
||||
return ALL_TRANSFERRED;
|
||||
} else {
|
||||
return PARTIAL_TRANSFER;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::receiveImageSet(ImageSet& imageSet) {
|
||||
int validRows = 0;
|
||||
bool complete = false;
|
||||
|
||||
std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
|
||||
while(!complete) {
|
||||
if(!receivePartialImageSet(imageSet, validRows, complete)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int time = static_cast<unsigned int>(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - startTime).count());
|
||||
if(time > 1000) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::receivePartialImageSet(ImageSet& imageSet,
|
||||
int& validRows, bool& complete) {
|
||||
unique_lock<recursive_mutex> lock(receiveMutex);
|
||||
|
||||
// Try to receive further image data if needed
|
||||
bool block = true;
|
||||
while(!protocol->imagesReceived() && receiveNetworkData(block)) {
|
||||
block = false;
|
||||
}
|
||||
|
||||
// Get received image
|
||||
return protocol->getPartiallyReceivedImageSet(imageSet, validRows, complete);
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::receiveNetworkData(bool block) {
|
||||
unique_lock<recursive_mutex> lock = block ?
|
||||
unique_lock<recursive_mutex>(receiveMutex) : unique_lock<recursive_mutex>(receiveMutex, std::try_to_lock);
|
||||
|
||||
if(clientSocket == INVALID_SOCKET) {
|
||||
return false; // Not connected
|
||||
}
|
||||
|
||||
// First send control messages if necessary
|
||||
sendPendingControlMessages();
|
||||
|
||||
if(!lock.owns_lock()) {
|
||||
// Waiting for the lock would block this call
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if the socket has data available
|
||||
if(!block && !selectSocket(true, false)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxLength = 0;
|
||||
char* buffer = reinterpret_cast<char*>(protocol->getNextReceiveBuffer(maxLength));
|
||||
|
||||
// Receive data
|
||||
sockaddr_in fromAddress;
|
||||
socklen_t fromSize = sizeof(fromAddress);
|
||||
|
||||
int bytesReceived = recvfrom(clientSocket, buffer, maxLength,
|
||||
0, reinterpret_cast<sockaddr*>(&fromAddress), &fromSize);
|
||||
|
||||
if(bytesReceived == 0 || (protType == ImageProtocol::PROTOCOL_TCP && bytesReceived < 0 && errno == WSAECONNRESET)) {
|
||||
// Connection closed
|
||||
disconnect();
|
||||
} else if(bytesReceived < 0 && errno != EWOULDBLOCK && errno != EINTR &&
|
||||
errno != ETIMEDOUT && errno != WSA_IO_PENDING && errno != WSAECONNRESET) {
|
||||
TransferException ex("Error reading from socket: " + string(strerror(errno)));
|
||||
throw ex;
|
||||
} else if(bytesReceived > 0) {
|
||||
protocol->processReceivedMessage(bytesReceived);
|
||||
if(protocol->newClientConnected()) {
|
||||
// We have just established a new connection
|
||||
memcpy(&remoteAddress, &fromAddress, sizeof(remoteAddress));
|
||||
}
|
||||
}
|
||||
|
||||
return bytesReceived > 0;
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::disconnect() {
|
||||
// We just need to forget the remote address in order to
|
||||
// disconnect
|
||||
unique_lock<recursive_mutex> recvLock(receiveMutex);
|
||||
unique_lock<recursive_mutex> sendLock(sendMutex);
|
||||
|
||||
if(clientSocket != INVALID_SOCKET && protType == ImageProtocol::PROTOCOL_TCP) {
|
||||
Networking::closeSocket(clientSocket);
|
||||
}
|
||||
memset(&remoteAddress, 0, sizeof(remoteAddress));
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::isConnected() const {
|
||||
unique_lock<recursive_mutex> lock(const_cast<recursive_mutex&>(sendMutex)); //either mutex will work
|
||||
|
||||
return remoteAddress.sin_family == AF_INET && protocol->isConnected();
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::sendNetworkMessage(const unsigned char* msg, int length) {
|
||||
int written = 0;
|
||||
if(protType == ImageProtocol::PROTOCOL_UDP) {
|
||||
sockaddr_in destAddr;
|
||||
SOCKET destSocket;
|
||||
{
|
||||
unique_lock<recursive_mutex> lock(sendMutex);
|
||||
destAddr = remoteAddress;
|
||||
destSocket = clientSocket;
|
||||
}
|
||||
|
||||
if(destAddr.sin_family != AF_INET) {
|
||||
return false; // Not connected
|
||||
}
|
||||
|
||||
written = sendto(destSocket, reinterpret_cast<const char*>(msg), length, 0,
|
||||
reinterpret_cast<sockaddr*>(&destAddr), sizeof(destAddr));
|
||||
} else {
|
||||
SOCKET destSocket;
|
||||
{
|
||||
unique_lock<recursive_mutex> lock(sendMutex);
|
||||
destSocket = clientSocket;
|
||||
}
|
||||
written = send(destSocket, reinterpret_cast<const char*>(msg), length, 0);
|
||||
}
|
||||
|
||||
unsigned long sendError = errno;
|
||||
|
||||
if(written < 0) {
|
||||
if(sendError == EAGAIN || sendError == EWOULDBLOCK || sendError == ETIMEDOUT) {
|
||||
// The socket is not yet ready for a new transfer
|
||||
return false;
|
||||
} else if(sendError == EPIPE) {
|
||||
// The connection has been closed
|
||||
disconnect();
|
||||
return false;
|
||||
} else {
|
||||
TransferException ex("Error sending network packet: " + string(strerror(sendError)));
|
||||
throw ex;
|
||||
}
|
||||
} else if(written != length) {
|
||||
if(protType == ImageProtocol::PROTOCOL_UDP) {
|
||||
// The message has been transmitted partially
|
||||
throw TransferException("Unable to transmit complete UDP message");
|
||||
} else {
|
||||
// For TCP we can transmit the remaining data later
|
||||
currentMsgOffset += written;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageTransfer::Pimpl::sendPendingControlMessages() {
|
||||
const unsigned char* controlMsgData = nullptr;
|
||||
int controlMsgLen = 0;
|
||||
|
||||
while(true) {
|
||||
unique_lock<recursive_mutex> lock(sendMutex);
|
||||
if(remoteAddress.sin_family != AF_INET) {
|
||||
return;
|
||||
}
|
||||
|
||||
controlMsgData = protocol->getNextControlMessage(controlMsgLen);
|
||||
|
||||
if(controlMsgData != nullptr) {
|
||||
sendNetworkMessage(controlMsgData, controlMsgLen);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ImageTransfer::Pimpl::getNumDroppedFrames() const {
|
||||
return protocol->getNumDroppedFrames();
|
||||
}
|
||||
|
||||
bool ImageTransfer::Pimpl::selectSocket(bool read, bool wait) {
|
||||
SOCKET sock;
|
||||
{
|
||||
unique_lock<recursive_mutex> lock(sendMutex); // Either mutex will do
|
||||
sock = clientSocket;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
tv.tv_sec = 0;
|
||||
if(wait) {
|
||||
tv.tv_usec = 100000;
|
||||
} else {
|
||||
tv.tv_usec = 0;
|
||||
}
|
||||
|
||||
if(select(sock+1, (read ? &fds : nullptr), (!read ? &fds : nullptr), nullptr, &tv) <= 0) {
|
||||
// The socket is currently not ready
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
// use poll() on non-Windows platform (glibc select() limitations)
|
||||
constexpr int timeoutMillisec = 100;
|
||||
pollfd pfd;
|
||||
pfd.fd = sock;
|
||||
pfd.events = POLLIN;
|
||||
if (poll(&pfd, 1, wait ? timeoutMillisec: 0) <= 0) {
|
||||
// The socket is currently not ready
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
// select (or poll) reported an event
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ImageTransfer::statusReport() {
|
||||
return pimpl->statusReport();
|
||||
}
|
||||
std::string ImageTransfer::Pimpl::statusReport() {
|
||||
return protocol->statusReport();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue