libvisiontransfer  5.1.0
datablockprotocol.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 <algorithm>
16 #include <iostream>
17 #include <cstring>
18 #include "visiontransfer/datablockprotocol.h"
19 #include "visiontransfer/exceptions.h"
20 
21 // Network headers
22 #ifdef _WIN32
23 #include <winsock2.h>
24 #else
25 #include <arpa/inet.h>
26 #endif
27 
28 using namespace std;
29 
30 DataBlockProtocol::DataBlockProtocol(ProtocolType protType, int maxUdpPacketSize)
31  : protType(protType), maxUdpPacketSize(maxUdpPacketSize),
32  transferDone(true), rawData(nullptr), rawValidBytes(0),
33  transferOffset(0), transferSize(0), transferSeqNum(0), overwrittenTransferData(0),
34  restoreTransferData(false), receiveDataSize(0), receiveSeqNum(0),
35  unprocessedMsgLength(0), receiveOffset(0), receptionDone(true) {
36  // Determine the maximum allowed payload size
37  if(protType == PROTOCOL_TCP) {
38  maxPayloadSize = MAX_TCP_BYTES_TRANSFER;
39  minPayloadSize = 0;
40  } else {
41  maxPayloadSize = maxUdpPacketSize - sizeof(unsigned short);
42  minPayloadSize = maxPayloadSize;
43  }
44 }
45 
47  transferSeqNum = 0;
48  transferDone = false;
49  restoreTransferData = false;
50 }
51 
53  transferDone = true;
54  restoreTransferData = false;
55 }
56 
57 void DataBlockProtocol::setTransferData(unsigned char* data, int size, int validBytes) {
58  rawData = data;
59  transferSize = size;
60  transferOffset = 0;
61  restoreTransferData = false;
62  rawValidBytes = min(transferSize, validBytes);
63 }
64 
66  if(validBytes >= transferSize) {
67  rawValidBytes = transferSize;
68  } else if(validBytes < static_cast<int>(sizeof(unsigned short))) {
69  rawValidBytes = 0;
70  } else {
71  // Always leave a small trailer of additional bytes to folow,
72  // as this one will be buffered for the next transfer.
73  rawValidBytes = validBytes - sizeof(unsigned short);
74  }
75 }
76 
77 const unsigned char* DataBlockProtocol::getTransferMessage(int& length) {
78  if(transferDone) {
79  length = 0;
80  return nullptr;
81  }
82 
83  // Determine the payload length
84  length = min(maxPayloadSize, rawValidBytes - transferOffset);
85  if(length == 0 || (length < minPayloadSize && rawValidBytes != transferSize)) {
86  length = 0;
87  return nullptr; //Not enough to send yet
88  }
89 
90  unsigned char* buffer = &rawData[transferOffset];
91  transferOffset += length;
92 
93  if(restoreTransferData) {
94  // Restore the original memory content that was overwritten with
95  // the previous sequence number
96  *reinterpret_cast<unsigned short*>(&buffer[0]) = overwrittenTransferData;
97  }
98 
99  if(protType == PROTOCOL_UDP) {
100  // For udp, we always append a sequence number
101  unsigned short* seqNumPtr = reinterpret_cast<unsigned short*>(&buffer[length]);
102  overwrittenTransferData = *seqNumPtr;
103  *seqNumPtr = htons(transferSeqNum);
104  length += sizeof(unsigned short);
105  restoreTransferData = true;
106  }
107 
108  transferSeqNum++;
109 
110  return buffer;
111 }
112 
114  return transferOffset >= transferSize;
115 }
116 
118  if(size != receiveDataSize) {
119  receiveDataSize = size;
120 
121  int bufferSize;
122 
123  // We increase the requested size to allow for one
124  // additional network message and the protocol overhead
125  bufferSize = size + getMaxReceptionSize() + sizeof(unsigned short);
126 
127  // Resize the buffer
128  receiveBuffer.resize(bufferSize);
129  }
130 }
131 
133  if(protType == PROTOCOL_TCP) {
134  return MAX_TCP_BYTES_TRANSFER;
135  } else {
136  return MAX_UDP_RECEPTION;
137  }
138 }
139 
140 unsigned char* DataBlockProtocol::getNextReceiveBuffer(int maxLength) {
141  if(static_cast<int>(receiveBuffer.size() - receiveOffset) < maxLength) {
142  throw ProtocolException("No more receive buffers available!");
143  }
144 
145  if(receptionDone) {
146  // Start receiving a new data block
147  receptionDone = false;
148  receiveOffset = 0;
149  receiveSeqNum = 0;
150  }
151 
152  return &receiveBuffer[receiveOffset];
153 }
154 
156  if(receiveOffset + length > static_cast<int>(receiveBuffer.size())) {
157  throw ProtocolException("Received message size is invalid!");
158  }
159 
160  if(length == 0) {
161  // Nothing received. Let's not call this an error.
162  return true;
163  } else if(protType == PROTOCOL_UDP) {
164  // UDP specific payload handling
165 
166  // Correct the length
167  length -= sizeof(unsigned short);
168 
169  // Extract the sequence number
170  unsigned short seqNum = ntohs(
171  *reinterpret_cast<unsigned short*>(&receiveBuffer[receiveOffset + length]));
172 
173  if(seqNum != static_cast<unsigned short>(receiveSeqNum)) {
174  // Sequence numbers don't match. Probably dropped a packet
175  resetReception();
176  return false;
177  }
178 
179  // Update sequence number
180  receiveSeqNum++;
181  } else {
182  // For TCP we might have some outstanding bytes from the
183  // previous transfer. Lets copy that part from a separate buffer.
184  if(unprocessedMsgLength != 0) {
185  if(length + unprocessedMsgLength > MAX_OUTSTANDING_BYTES) {
186  throw ProtocolException("Received too much data!");
187  }
188 
189  ::memmove(&receiveBuffer[unprocessedMsgLength], &receiveBuffer[0], length);
190  ::memcpy(&receiveBuffer[0], &unprocessedMsgPart[0], unprocessedMsgLength);
191  length += unprocessedMsgLength;
192  unprocessedMsgLength = 0;
193  }
194 
195  // The message might also contain extra bytes for the next
196  // transfer. Lets copy that part into a separate buffer.
197  if(receiveOffset + length > receiveDataSize) {
198  int newLength = static_cast<int>(receiveDataSize - receiveOffset);
199 
200  if(unprocessedMsgLength != 0 || length - newLength > MAX_OUTSTANDING_BYTES) {
201  throw ProtocolException("Received too much data!");
202  }
203 
204  unprocessedMsgLength = length - newLength;
205  ::memcpy(unprocessedMsgPart, &receiveBuffer[receiveOffset + newLength], unprocessedMsgLength);
206 
207  length = newLength;
208  }
209  }
210 
211  // Update the receive buffer offset
212  receiveOffset += length;
213 
214  if(receiveOffset > static_cast<int>(receiveBuffer.size())) {
215  throw ProtocolException("Receive buffer overflow!");
216  }
217 
218  return true;
219 }
220 
222  receptionDone = true;
223 }
224 
226  receiveOffset = 0;
227  receiveSeqNum = 0;
228 }
229 
230 unsigned char* DataBlockProtocol::getReceivedData(int& length) {
231  length = receiveOffset;
232  return &receiveBuffer[0];
233 }
Exception class that is used for all protocol exceptions.
Definition: exceptions.h:23
void setReceiveDataSize(int size)
Sets the total size of the data that shall be received.
const unsigned char * getTransferMessage(int &length)
Gets the next network message for the current transfer.
DataBlockProtocol(ProtocolType protType, int maxUdpPacketSize)
Creates a new instance.
unsigned char * getReceivedData(int &length)
Returns the data that has been received for the current data block.
void resetTransfer()
Stops the current transfer.
unsigned char * getNextReceiveBuffer(int maxLength)
Gets a buffer for receiving the next network message.
int getMaxReceptionSize() const
Returns the maximum paload size that can be recedived.
void resetReception()
Resets the message reception.
bool transferComplete()
Returns true if the current transfer has been completed.
bool processReceivedMessage(int length)
Handles a received network message.
void setTransferData(unsigned char *data, int size, int validBytes=0x7FFFFFFF)
Sets a new chunk of data that should be transferred.
void startTransfer()
Starts the transfer of a new data block.
void finishReception()
Finishes reception of the current data block.
void setTransferValidBytes(int validBytes)
Updates the number of valid bytes in a partial transmission.
Nerian Vision Technologies