libvisiontransfer  4.1.5
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): protType(protType),
31  transferDone(true), rawData(nullptr), rawValidBytes(0),
32  transferOffset(0), transferSize(0), transferSeqNum(0), overwrittenTransferData(0),
33  restoreTransferData(false), receiveDataSize(0), receiveSeqNum(0),
34  unprocessedMsgLength(0), receiveOffset(0), receptionDone(true) {
35  // Determine the maximum allowed payload size
36  if(protType == PROTOCOL_TCP) {
37  maxPayloadSize = MAX_TCP_BYTES_TRANSFER;
38  minPayloadSize = 0;
39  } else {
40  maxPayloadSize = MAX_UDP_BYTES_TRANSFER - sizeof(unsigned short);
41  minPayloadSize = MIN_UDP_BYTES_TRANSFER;
42  }
43 }
44 
46  transferSeqNum = 0;
47  transferDone = false;
48  restoreTransferData = false;
49 }
50 
52  transferDone = true;
53  restoreTransferData = false;
54 }
55 
56 void DataBlockProtocol::setTransferData(unsigned char* data, int size, int validBytes) {
57  rawData = data;
58  transferSize = size;
59  transferOffset = 0;
60  restoreTransferData = false;
61  rawValidBytes = min(transferSize, validBytes);
62 }
63 
65  if(validBytes >= transferSize) {
66  rawValidBytes = transferSize;
67  } else if(validBytes < static_cast<int>(sizeof(unsigned short))) {
68  rawValidBytes = 0;
69  } else {
70  // Always leave a small trailer of additional bytes to folow,
71  // as this one will be buffered for the next transfer.
72  rawValidBytes = validBytes - sizeof(unsigned short);
73  }
74 }
75 
76 const unsigned char* DataBlockProtocol::getTransferMessage(int& length) {
77  if(transferDone) {
78  length = 0;
79  return nullptr;
80  }
81 
82  // Determine the payload length
83  length = min(maxPayloadSize, rawValidBytes - transferOffset);
84  if(length == 0 || (length < minPayloadSize && rawValidBytes != transferSize)) {
85  length = 0;
86  return nullptr; //Not enough to send yet
87  }
88 
89  unsigned char* buffer = &rawData[transferOffset];
90  transferOffset += length;
91 
92  if(restoreTransferData) {
93  // Restore the original memory content that was overwritten with
94  // the previous sequence number
95  *reinterpret_cast<unsigned short*>(&buffer[0]) = overwrittenTransferData;
96  }
97 
98  if(protType == PROTOCOL_UDP) {
99  // For udp, we always append a sequence number
100  unsigned short* seqNumPtr = reinterpret_cast<unsigned short*>(&buffer[length]);
101  overwrittenTransferData = *seqNumPtr;
102  *seqNumPtr = htons(transferSeqNum);
103  length += sizeof(unsigned short);
104  restoreTransferData = true;
105  }
106 
107  transferSeqNum++;
108 
109  return buffer;
110 }
111 
113  return transferOffset >= transferSize;
114 }
115 
117  if(size != receiveDataSize) {
118  receiveDataSize = size;
119 
120  int bufferSize;
121 
122  // We increase the requested size to allow for one
123  // additional network message and the protocol overhead
124  if(protType == PROTOCOL_TCP) {
125  bufferSize = size + MAX_TCP_BYTES_TRANSFER + sizeof(unsigned short);
126  } else {
127  bufferSize = size + MAX_UDP_BYTES_TRANSFER + sizeof(unsigned short);
128  }
129 
130  // Resize the buffer
131  receiveBuffer.resize(bufferSize);
132  }
133 }
134 
135 unsigned char* DataBlockProtocol::getNextReceiveBuffer(int maxLength) {
136  if(static_cast<int>(receiveBuffer.size() - receiveOffset) < maxLength) {
137  throw ProtocolException("No more receive buffers available!");
138  }
139 
140  if(receptionDone) {
141  // Start receiving a new data block
142  receptionDone = false;
143  receiveOffset = 0;
144  receiveSeqNum = 0;
145  }
146 
147  return &receiveBuffer[receiveOffset];
148 }
149 
151  if(receiveOffset + length > static_cast<int>(receiveBuffer.size())) {
152  throw ProtocolException("Received message size is invalid!");
153  }
154 
155  if(length == 0) {
156  // Nothing received. Let's not call this an error.
157  return true;
158  } else if(protType == PROTOCOL_UDP) {
159  // UDP specific payload handling
160 
161  // Correct the length
162  length -= sizeof(unsigned short);
163 
164  // Extract the sequence number
165  unsigned short seqNum = ntohs(
166  *reinterpret_cast<unsigned short*>(&receiveBuffer[receiveOffset + length]));
167 
168  if(seqNum != static_cast<unsigned short>(receiveSeqNum)) {
169  // Sequence numbers don't match. Probably dropped a packet
170  resetReception();
171  return false;
172  }
173 
174  // Update sequence number
175  receiveSeqNum++;
176  } else {
177  // For TCP we might have some outstanding bytes from the
178  // previous transfer. Lets copy that part from a separate buffer.
179  if(unprocessedMsgLength != 0) {
180  if(length + unprocessedMsgLength > MAX_OUTSTANDING_BYTES) {
181  throw ProtocolException("Received too much data!");
182  }
183 
184  ::memmove(&receiveBuffer[unprocessedMsgLength], &receiveBuffer[0], length);
185  ::memcpy(&receiveBuffer[0], &unprocessedMsgPart[0], unprocessedMsgLength);
186  length += unprocessedMsgLength;
187  unprocessedMsgLength = 0;
188  }
189 
190  // The message might also contain extra bytes for the next
191  // transfer. Lets copy that part into a separate buffer.
192  if(receiveOffset + length > receiveDataSize) {
193  int newLength = static_cast<int>(receiveDataSize - receiveOffset);
194 
195  if(unprocessedMsgLength != 0 || length - newLength > MAX_OUTSTANDING_BYTES) {
196  throw ProtocolException("Received too much data!");
197  }
198 
199  unprocessedMsgLength = length - newLength;
200  ::memcpy(unprocessedMsgPart, &receiveBuffer[receiveOffset + newLength], unprocessedMsgLength);
201 
202  length = newLength;
203  }
204  }
205 
206  // Update the receive buffer offset
207  receiveOffset += length;
208 
209  if(receiveOffset > static_cast<int>(receiveBuffer.size())) {
210  throw ProtocolException("Receive buffer overflow!");
211  }
212 
213  return true;
214 }
215 
217  receptionDone = true;
218 }
219 
221  receiveOffset = 0;
222  receiveSeqNum = 0;
223 }
224 
225 unsigned char* DataBlockProtocol::getReceivedData(int& length) {
226  length = receiveOffset;
227  return &receiveBuffer[0];
228 }
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.
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.
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