libvisiontransfer  8.3.0
datablockprotocol.cpp
1 /*******************************************************************************
2  * Copyright (c) 2020 Nerian Vision GmbH
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 
19 #include <iomanip>
20 #include <sstream>
21 
22 #include "visiontransfer/datablockprotocol.h"
23 #include "visiontransfer/exceptions.h"
24 
25 // Network headers
26 #ifdef _WIN32
27 #include <winsock2.h>
28 #undef min
29 #undef max
30 #else
31 #include <arpa/inet.h>
32 #endif
33 
34 #define LOG_ERROR(expr)
35 //#define LOG_ERROR(expr) std::cerr << "DataBlockProtocol: " << expr << std::endl
36 
37 using namespace std;
38 using namespace visiontransfer;
39 using namespace visiontransfer::internal;
40 
41 namespace visiontransfer {
42 namespace internal {
43 
44 DataBlockProtocol::DataBlockProtocol(bool server, ProtocolType protType, int maxUdpPacketSize)
45  : isServer(server), protType(protType),
46  transferDone(true),
47  overwrittenTransferData{0},
48  overwrittenTransferIndex{-1},
49  overwrittenTransferBlock{-1},
50  transferHeaderData{nullptr},
51  transferHeaderSize{0},
52  totalBytesCompleted{0}, totalTransferSize{0},
53  waitingForMissingSegments(false),
54  totalReceiveSize(0), connectionConfirmed(false),
55  confirmationMessagePending(false), eofMessagePending(false),
56  clientConnectionPending(false), resendMessagePending(false),
57  lastRemoteHostActivity(), lastSentHeartbeat(),
58  lastReceivedHeartbeat(std::chrono::steady_clock::now()),
59  finishedReception(false), droppedReceptions(0),
60  completedReceptions(0), lostSegmentRate(0.0), lostSegmentBytes(0),
61  unprocessedMsgLength(0), headerReceived(false) {
62  // Determine the maximum allowed payload size
63  if(protType == PROTOCOL_TCP) {
64  maxPayloadSize = MAX_TCP_BYTES_TRANSFER - sizeof(SegmentHeaderTCP);
65  minPayloadSize = 0;
66  } else {
67  maxPayloadSize = maxUdpPacketSize - sizeof(SegmentHeaderUDP);
68  minPayloadSize = maxPayloadSize;
69  }
70  zeroStructures();
71  resizeReceiveBuffer();
72  resetReception(false);
73 }
74 void DataBlockProtocol::splitRawOffset(int rawSegmentOffset, int& dataBlockID, int& segmentOffset) {
75  int selector = (rawSegmentOffset >> 28) & 0xf;
76  dataBlockID = selector & 0x7; // Note: 0x8 bit is reserved for now
77  segmentOffset = rawSegmentOffset & 0x0FFFffff;
78 }
79 
80 int DataBlockProtocol::mergeRawOffset(int dataBlockID, int segmentOffset, int reserved_defaults0) {
81  return ((reserved_defaults0 & 1) << 31) | ((dataBlockID & 0x07) << 28) | (segmentOffset & 0x0FFFffff);
82 }
83 
84 void DataBlockProtocol::zeroStructures() {
85  for (int i=0; i<MAX_DATA_BLOCKS; ++i) {
86  rawDataArr[i] = nullptr;
87  rawDataArrStrideHackOrig[i] = 0;
88  rawDataArrStrideHackRepl[i] = 0;
89  rawValidBytes[i] = 0;
90  transferOffset[i] = 0;
91  transferSize[i] = 0;
92  }
93  std::memset(overwrittenTransferData, 0, sizeof(overwrittenTransferData));
94  overwrittenTransferIndex = -1;
95  overwrittenTransferBlock = -1;
96  lastTransmittedBlock = -1;
97  receiveOffset = 0;
98  numReceptionBlocks = 0;
99 }
100 
102  transferDone = true;
103  overwrittenTransferIndex = -1;
104  overwrittenTransferBlock = -1;
105  totalBytesCompleted = 0;
106  totalTransferSize = 0;
107  numTransferBlocks = 0;
108  missingTransferSegments.clear();
109 }
110 
111 void DataBlockProtocol::setTransferBytes(int block, long bytes) {
112  if (transferHeaderData == nullptr) {
113  throw ProtocolException("Tried to set data block size before initializing header!");
114  } else if (block >= numTransferBlocks) {
115  throw ProtocolException("Request to set data block size - block index too high!");
116  }
117  transferSize[block] = bytes;
118  HeaderPreamble* hp = reinterpret_cast<HeaderPreamble*>(transferHeaderData);
119  hp->netTransferSizes[block] = htonl(bytes);
120 }
121 
122 void DataBlockProtocol::setTransferHeader(unsigned char* data, int headerSize, int blocks) {
123  if(!transferDone && transferOffset > 0) {
124  throw ProtocolException("Header data set while transfer is active!");
125  } else if(headerSize + 9 > static_cast<int>(sizeof(controlMessageBuffer))) {
126  throw ProtocolException("Transfer header is too large!");
127  }
128 
129  numTransferBlocks = blocks;
130 
131  transferDone = false;
132  for (int i=0; i<MAX_DATA_BLOCKS; ++i) {
133  this->transferSize[i] = 0; // must be set via setRawTransferBytes()
134  }
135 
136  int headerBaseOffset = sizeof(HeaderPreamble);
137 
138  transferHeaderData = &data[-headerBaseOffset];
139  HeaderPreamble* ourHeader = reinterpret_cast<HeaderPreamble*>(transferHeaderData);
140 
141  unsigned short netHeaderSize = htons(static_cast<unsigned short>(headerSize));
142  ourHeader->netHeaderSize = netHeaderSize;
143  ourHeader->netTransferSizeDummy = htonl(-1); // clashes on purpose with old recipients
144 
145  headerSize += headerBaseOffset;
146 
147  if(protType == PROTOCOL_UDP) {
148  // In UDP mode we still need to make this a control message
149  transferHeaderData[headerSize++] = HEADER_MESSAGE;
150  transferHeaderData[headerSize++] = 0xFF;
151  transferHeaderData[headerSize++] = 0xFF;
152  transferHeaderData[headerSize++] = 0xFF;
153  transferHeaderData[headerSize++] = 0xFF;
154  }
155 
156  transferHeaderSize = headerSize;
157 }
158 
159 void DataBlockProtocol::setTransferData(int block, unsigned char* data, int validBytes) {
160  if(transferHeaderSize == 0 || transferHeaderData == nullptr) {
161  throw ProtocolException("The transfer header has not yet been set!");
162  }
163 
164  transferDone = false;
165  rawDataArr[block] = data;
166  transferOffset[block] = 0;
167  overwrittenTransferIndex = -1;
168  overwrittenTransferBlock = -1;
169  rawValidBytes[block] = min(transferSize[block], validBytes);
170  totalBytesCompleted = 0;
171 }
172 
173 void DataBlockProtocol::setTransferValidBytes(int block, int validBytes) {
174  if(validBytes >= transferSize[block]) {
175  rawValidBytes[block] = transferSize[block];
176  } else if(validBytes < static_cast<int>(sizeof(int))) {
177  rawValidBytes[block] = 0;
178  } else {
179  rawValidBytes[block] = validBytes;
180  }
181 }
182 
183 std::string DataBlockProtocol::statusReport() {
184  std::stringstream ss;
185  ss << "DataBlockProtocol, blocks=" << numTransferBlocks << ": ";
186  for (int i=0; i<numTransferBlocks; ++i) {
187  ss << i << ":(len " << transferSize[i] << " ofs " << transferOffset[i] << " rawvalid " << rawValidBytes[i] << ") ";
188  }
189  ss << " total done: " << totalBytesCompleted << "/" << totalTransferSize;
190  return ss.str();
191 }
192 
193 const unsigned char* DataBlockProtocol::getTransferMessage(int& length) {
194  if(transferDone || rawValidBytes == 0) {
195  // No more data to be transferred
196  length = 0;
197  return nullptr;
198  }
199 
200  // For TCP we always send the header first
201  if(protType == PROTOCOL_TCP && !anyPayloadReceived() && transferHeaderData != nullptr) {
202  length = transferHeaderSize;
203  const unsigned char* ret = transferHeaderData;
204  transferHeaderData = nullptr;
205  return ret;
206  }
207 
208  // The transfer buffer might have been altered by the previous transfer
209  // and first needs to be restored
210  restoreTransferBuffer();
211 
212  // Determine which data segment to transfer next
213  int block = -1, offset = -1;
214  getNextTransferSegment(block, offset, length);
215  if(length == 0) {
216  return nullptr;
217  }
218 
219  if(protType == PROTOCOL_UDP) {
220  // For udp, we always append a segment offset
221  overwrittenTransferBlock = block;
222  overwrittenTransferIndex = offset + length;
223  SegmentHeaderUDP* segmentHeader = reinterpret_cast<SegmentHeaderUDP*>(&rawDataArr[block][offset + length]);
224  std::memcpy(overwrittenTransferData, segmentHeader, sizeof(SegmentHeaderUDP));
225  segmentHeader->segmentOffset = static_cast<int>(htonl(mergeRawOffset(block, offset)));
226  length += sizeof(SegmentHeaderUDP);
227  lastTransmittedBlock = block;
228  return &rawDataArr[block][offset];
229  } else {
230  // For tcp, we *PRE*prend the header consisting of segment offset plus the packet payload size
231  int headerOffset = offset - sizeof(SegmentHeaderTCP);
232  overwrittenTransferBlock = block;
233  overwrittenTransferIndex = headerOffset;
234  SegmentHeaderTCP* segmentHeader = reinterpret_cast<SegmentHeaderTCP*>(&rawDataArr[block][headerOffset]);
235  std::memcpy(overwrittenTransferData, segmentHeader, sizeof(SegmentHeaderTCP));
236  segmentHeader->fragmentSize = htons(length);
237  segmentHeader->segmentOffset = static_cast<int>(htonl(mergeRawOffset(block, offset)));
238  length += sizeof(SegmentHeaderTCP);
239  lastTransmittedBlock = block;
240  return &rawDataArr[block][headerOffset];
241  }
242 
243 }
244 
245 void DataBlockProtocol::getNextTransferSegment(int& block, int& offset, int& length) {
246  if(missingTransferSegments.size() == 0) {
247  // Select from block with the most unsent data
248  int sendBlock = 0, amount = 0;
249  for (int i=0; i<numTransferBlocks; ++i) {
250  int avail = std::min(transferSize[i], rawValidBytes[i]);
251  avail -= transferOffset[i];
252  if (avail > amount) {
253  amount = avail;
254  sendBlock = i;
255  }
256  }
257  length = std::min(maxPayloadSize, amount);
258  if(length == 0 || (length < minPayloadSize && rawValidBytes[sendBlock] != transferSize[sendBlock])) {
259  length = 0;
260  return;
261  }
262 
263  block = sendBlock;
264  offset = transferOffset[sendBlock];
265  transferOffset[sendBlock] += length; // for next transfer
266  if (protType == PROTOCOL_UDP) {
267  bool complete = true;
268  for (int i=0; i<numTransferBlocks; ++i) {
269  if (transferOffset[i] < transferSize[i]) {
270  complete = false;
271  break;
272  }
273  }
274  if (complete) {
275  eofMessagePending = true;
276  }
277  }
278  } else {
279  // This is a segment that is re-transmitted due to packet loss
280  splitRawOffset(missingTransferSegments.front().first, block, offset);
281  length = std::min(maxPayloadSize, missingTransferSegments.front().second);
282  LOG_ERROR("Re-transmitting: " << offset << " - " << (offset + length));
283 
284  int remaining = missingTransferSegments[0].second - length;
285  if(remaining == 0) {
286  // The segment is competed
287  missingTransferSegments.pop_front();
288  } else {
289  // The segment is only partially complete
290  missingTransferSegments.front().first += length;
291  missingTransferSegments.front().second = remaining;
292  }
293  }
294 }
295 
296 void DataBlockProtocol::restoreTransferBuffer() {
297  if(overwrittenTransferBlock >= 0) {
298  if(protType == PROTOCOL_UDP) {
299  std::memcpy(&rawDataArr[overwrittenTransferBlock][overwrittenTransferIndex], overwrittenTransferData, sizeof(SegmentHeaderUDP));
300  } else {
301  std::memcpy(&rawDataArr[overwrittenTransferBlock][overwrittenTransferIndex], overwrittenTransferData, sizeof(SegmentHeaderTCP));
302  }
303  }
304  overwrittenTransferIndex = -1;
305  overwrittenTransferBlock = -1;
306 }
307 
309  for (int i=0; i<numTransferBlocks; ++i) {
310  if (transferOffset[i] < transferSize[i]) return false;
311  }
312  return !eofMessagePending;
313 }
314 
316  if(protType == PROTOCOL_TCP) {
317  return MAX_TCP_BYTES_TRANSFER;
318  } else {
319  return MAX_UDP_RECEPTION;
320  }
321 }
322 
323 unsigned char* DataBlockProtocol::getNextReceiveBuffer(int maxLength) {
324  return &receiveBuffer[receiveOffset];
325 }
326 
328  transferComplete = false;
329  if(length <= 0) {
330  return; // Nothing received
331  }
332 
333  if(finishedReception) {
334  // First reset for next frame
335  resetReception(false);
336  }
337 
338  if(protType == PROTOCOL_UDP) {
339  processReceivedUdpMessage(length, transferComplete);
340  } else {
341  processReceivedTcpMessage(length, transferComplete);
342  }
343 
344  transferComplete = finishedReception;
345 }
346 
347 void DataBlockProtocol::processReceivedUdpMessage(int length, bool& transferComplete) {
348  if(length < static_cast<int>(sizeof(int)) ||
349  0 + length > static_cast<int>(receiveBuffer.size())) {
350  throw ProtocolException("Received message size is invalid!");
351  }
352 
353  // Extract the sequence number
354  int rawSegmentOffset = ntohl(*reinterpret_cast<int*>(
355  &receiveBuffer[0 + length - sizeof(int)]));
356  // for holding the offset with blanked-out channel index
357  int dataBlockID, segmentOffset;
358  splitRawOffset(rawSegmentOffset, dataBlockID, segmentOffset);
359 
360  if(rawSegmentOffset == static_cast<int>(0xFFFFFFFF)) {
361  // This is a control packet
362  processControlMessage(length);
363  } else if(headerReceived) {
364  // Correct the length by subtracting the size of the segment offset
365  int realPayloadOffset = 0;
366  int payloadLength = length - sizeof(int);
367 
368  if(segmentOffset != blockReceiveOffsets[dataBlockID]) {
369  // The segment offset doesn't match what we expected. Probably
370  // a packet was dropped
371  if(!waitingForMissingSegments && //receiveOffset > 0 &&
372  segmentOffset > blockReceiveOffsets[dataBlockID]
373  && segmentOffset + payloadLength < (int)blockReceiveBuffers[dataBlockID].size()) {
374  // We can just ask for a retransmission of this packet
375  LOG_ERROR("Missing segment: " << blockReceiveOffsets[dataBlockID] << " - " << segmentOffset
376  << " (" << missingReceiveSegments.size() << ")");
377 
378  MissingReceiveSegment missingSeg;
379  missingSeg.offset = mergeRawOffset(dataBlockID, blockReceiveOffsets[dataBlockID]);
380  missingSeg.length = segmentOffset - blockReceiveOffsets[dataBlockID];
381  missingSeg.isEof = false;
382  lostSegmentBytes += missingSeg.length;
383  missingReceiveSegments.push_back(missingSeg);
384 
385  // Move the received data to the right place in the buffer
386  memcpy(&blockReceiveBuffers[dataBlockID][segmentOffset], &receiveBuffer[0 + realPayloadOffset], payloadLength);
387  // Advance block receive offset
388  blockReceiveOffsets[dataBlockID] = segmentOffset + payloadLength;
389  } else {
390  // In this case we cannot recover from the packet loss or
391  // we just didn't get the EOF packet and everything is
392  // actually fine
393  resetReception(blockReceiveOffsets[0] > 0);
394  if(segmentOffset > 0 ) {
395  if(blockReceiveOffsets[dataBlockID] > 0) {
396  LOG_ERROR("Resend failed!");
397  }
398  return;
399  } else {
400  LOG_ERROR("Missed EOF message!");
401  }
402  }
403  } else {
404  if ((realPayloadOffset+payloadLength) > (int)receiveBuffer.size()) {
405  throw ProtocolException("Received out-of-bound data.");
406  }
407 
408  // append to correct block buffer
409  memcpy(&blockReceiveBuffers[dataBlockID][segmentOffset], &receiveBuffer[0 + realPayloadOffset], payloadLength);
410  // advance the expected next data offset for this block
411  blockReceiveOffsets[dataBlockID] = segmentOffset + payloadLength;
412  if (waitingForMissingSegments) {
413  // segment extends the currently valid region (suspended once we missed out first segment)
414  if ((missingReceiveSegments.size() == 1) && (missingReceiveSegments.front().length <= payloadLength)) {
415  // last gap closed by this segment
416  blockValidSize[dataBlockID] = blockReceiveSize[dataBlockID];
417  } else {
418  blockValidSize[dataBlockID] = segmentOffset + payloadLength;
419  }
420  } else if (missingReceiveSegments.size() == 0) {
421  blockValidSize[dataBlockID] = segmentOffset + payloadLength;
422  }
423  }
424 
425  if(segmentOffset == 0 && dataBlockID == 0) {
426  // This is the beginning of a new frame
427  lastRemoteHostActivity = std::chrono::steady_clock::now();
428  }
429 
430  // Try to fill missing regions
431  integrateMissingUdpSegments(dataBlockID, segmentOffset, payloadLength);
432  }
433 }
434 
435 void DataBlockProtocol::integrateMissingUdpSegments(int block, int lastSegmentOffset, int lastSegmentSize) {
436  if(waitingForMissingSegments) {
437  // Things get more complicated when re-transmitting dropped packets
438  int checkBlock, checkOffset;
439  MissingReceiveSegment& firstSeg = missingReceiveSegments.front();
440  splitRawOffset(firstSeg.offset, checkBlock, checkOffset);
441  if(lastSegmentOffset != checkOffset) {
442  LOG_ERROR("Received invalid resend: " << lastSegmentOffset);
443  resetReception(true);
444  } else {
445  firstSeg.offset += lastSegmentSize;
446  firstSeg.length -= lastSegmentSize;
447  if(firstSeg.length == 0) {
448  missingReceiveSegments.pop_front();
449  }
450 
451  if(missingReceiveSegments.size() == 0) {
452  waitingForMissingSegments = false;
453  finishedReception = true;
454  } else {
455  blockReceiveOffsets[block] = missingReceiveSegments.front().offset;
456  }
457  }
458  }
459 }
460 
461 void DataBlockProtocol::processReceivedTcpMessage(int length, bool& transferComplete) {
462  // In TCP mode the header must be the first data item to be transmitted
463  if(!headerReceived) {
464  int totalHeaderSize = parseReceivedHeader(length, 0);
465  if(totalHeaderSize == 0) {
466  // Not yet enough data. Keep on buffering.
467  receiveOffset += length; // append in next recv
468  return;
469  } else {
470  // Header successfully parsed
471  // Move the remaining data to the beginning of the buffer
472  length -= totalHeaderSize;
473  // The rest is the first [part of] buffer segment data
474 
475  if(length == 0) {
476  return; // No more data remaining
477  }
478 
479  int movelength = receiveOffset + length; // also move the old stuff
480  ::memmove(&receiveBuffer[0], &receiveBuffer[totalHeaderSize], movelength);
481  receiveOffset = movelength; // append in next recv
482  }
483  } else {
484  receiveOffset += length; // modified below if complete chunks are present
485  }
486 
487  if (legacyTransfer) {
488  // Legacy TCP transfer: no segment headers, just raw data for block 0, up to the expected size
489  int remainingSize = blockReceiveSize[0] - blockValidSize[0];
490  int availableSize = std::min(receiveOffset, remainingSize);
491  // Update actual target buffer
492  std::memcpy(&blockReceiveBuffers[0][blockReceiveOffsets[0]], &receiveBuffer[0], availableSize);
493  blockReceiveOffsets[0] += availableSize;
494  blockValidSize[0] = blockReceiveOffsets[0];
495  // Extra data, store at buffer start for next reception to append to
496  if (receiveOffset <= remainingSize) {
497  // Start next reception at recv buffer start
498  receiveOffset = 0;
499  } else {
500  // Mark next reception to append to unhandled data remainder
501  std::memmove(&receiveBuffer[0], &receiveBuffer[remainingSize], availableSize - remainingSize);
502  receiveOffset = availableSize - remainingSize;
503  }
504  } else {
505  // Parse the SegmentHeaderTCP (if present) to see if a full fragment is present
506  int ofs = 0;
507  while ((receiveOffset - ofs) >= (int) sizeof(SegmentHeaderTCP)) {
508  SegmentHeaderTCP* header = reinterpret_cast<SegmentHeaderTCP*>(&receiveBuffer[ofs]);
509  int fragsize = ntohs(header->fragmentSize);
510  int rawSegmentOffset = ntohl(header->segmentOffset);
511  int block, offset;
512  splitRawOffset(rawSegmentOffset, block, offset);
513  if (block == 7) { // Block 7 is reserved; control message (the next header), stop moving image data
514  break;
515  }
516  if ((receiveOffset - ofs) >= (fragsize + (int) sizeof(SegmentHeaderTCP))) {
517  // Incorporate fragment
518  // assert here that offset==blockReceiveOffsets[block]
519  if (offset != blockReceiveOffsets[block]) {
520  throw ProtocolException("Received invalid header!");
521  }
522  std::memcpy(&blockReceiveBuffers[block][blockReceiveOffsets[block]], &receiveBuffer[ofs+sizeof(SegmentHeaderTCP)], fragsize);
523  blockReceiveOffsets[block] += fragsize;
524  blockValidSize[block] = blockReceiveOffsets[block];
525  // Advance to next potential chunk
526  ofs += fragsize + sizeof(SegmentHeaderTCP);
527  } else {
528  // Fragment incomplete, will be appended to in next recv (offset increased above)
529  break;
530  }
531  }
532  if (ofs > 0) {
533  // Move start of next unaccounted-for fragment to start of buffer
534  std::memmove(&receiveBuffer[0], &receiveBuffer[ofs], receiveOffset - ofs);
535  receiveOffset -= ofs; // and shift append position accordingly
536  }
537  }
538 
539  // Determine whether all buffers are filled now
540  bool complete = true;
541  for (int i=0; i<numReceptionBlocks; ++i) {
542  if (blockReceiveOffsets[i] < blockReceiveSize[i]) {
543  complete = false;
544  break;
545  }
546  }
547  finishedReception = complete;
548 
549 }
550 
551 int DataBlockProtocol::parseReceivedHeader(int length, int offset) {
552  int headerExtraBytes = 6; // see below
553 
554  if(length < headerExtraBytes) {
555  return 0;
556  }
557 
558  unsigned short headerSize = ntohs(*reinterpret_cast<unsigned short*>(&receiveBuffer[offset]));
559  if (length < (headerExtraBytes + headerSize)) {
560  return 0;
561  }
562  totalReceiveSize = static_cast<int>(ntohl(*reinterpret_cast<unsigned int*>(&receiveBuffer[offset + 2])));
563 
564  if (totalReceiveSize >= 0) { // old-style single block transfer
565  legacyTransfer = true;
566  headerExtraBytes = 6;
567  numReceptionBlocks = 1; // ONE interleaved buffer
568  blockReceiveSize[0] = totalReceiveSize;
569  } else { // marked -1 for new-style multi block transfer
570  legacyTransfer = false;
571  headerExtraBytes = static_cast<int>(sizeof(HeaderPreamble));
572  HeaderPreamble* header = reinterpret_cast<HeaderPreamble*>(&receiveBuffer[offset]);
573  numReceptionBlocks = 0;
574  totalReceiveSize = 0;
575  for (int i=0; i<MAX_DATA_BLOCKS; ++i) {
576  int s = ntohl(header->netTransferSizes[i]);
577  if (s > 0) {
578  blockReceiveSize[i] = s;
579  numReceptionBlocks++;
580  totalReceiveSize += s;
581  } else {
582  // first non-positive payload size signals end of blocks
583  //break;
584  }
585  }
586  }
587 
588  if (numReceptionBlocks==0) throw std::runtime_error("Received a transfer with zero blocks");
589  if (numReceptionBlocks > MAX_DATA_BLOCKS) throw std::runtime_error("Received a transfer with too many blocks");
590 
591  if(headerSize + headerExtraBytes > static_cast<int>(receiveBuffer.size())
592  || totalReceiveSize < 0 || headerSize + headerExtraBytes > length ) {
593  throw ProtocolException("Received invalid header!");
594  }
595 
596  headerReceived = true;
597  receivedHeader.assign(receiveBuffer.begin() + offset + headerExtraBytes,
598  receiveBuffer.begin() + offset + headerSize + headerExtraBytes);
599  resizeReceiveBuffer();
600 
601  return headerSize + headerExtraBytes;
602 }
603 
605  numReceptionBlocks = 0;
606  headerReceived = false;
607  missingReceiveSegments.clear();
608  receivedHeader.clear();
609  waitingForMissingSegments = false;
610  totalReceiveSize = 0;
611  finishedReception = false;
612  lostSegmentBytes = 0;
613  for (int i=0; i<MAX_DATA_BLOCKS; ++i) {
614  blockReceiveOffsets[i] = 0;
615  blockValidSize[i] = 0;
616  }
617  if(dropped) {
618  droppedReceptions++;
619  }
620 }
621 
622 unsigned char* DataBlockProtocol::getReceivedData(int& length) {
623  length = 0;
624  if(missingReceiveSegments.size() > 0) {
625  length = min(length, missingReceiveSegments[0].offset);
626  }
627  return &receiveBuffer[0];
628 }
629 
630 unsigned char* DataBlockProtocol::getReceivedHeader(int& length) {
631  if(receivedHeader.size() > 0) {
632  length = static_cast<int>(receivedHeader.size());
633  return &receivedHeader[0];
634  } else {
635  return nullptr;
636  }
637 }
638 
639 bool DataBlockProtocol::processControlMessage(int length) {
640  if(length < static_cast<int>(sizeof(int) + 1)) {
641  return false;
642  }
643 
644  int payloadLength = length - sizeof(int) - 1;
645  switch(receiveBuffer[0 + payloadLength]) {
646  case CONFIRM_MESSAGE:
647  // Our connection request has been accepted
648  connectionConfirmed = true;
649  break;
650  case CONNECTION_MESSAGE:
651  // We establish a new connection
652  connectionConfirmed = true;
653  confirmationMessagePending = true;
654  clientConnectionPending = true;
655 
656  // A connection request is just as good as a heartbeat
657  lastReceivedHeartbeat = std::chrono::steady_clock::now();
658  break;
659  case HEADER_MESSAGE: {
660  if (anyPayloadReceived()) {
661  if (allBlocksDone()) {
662  LOG_ERROR("No EOF message received!");
663  } else {
664  LOG_ERROR("Received header too late/early!");
665  }
666  resetReception(true);
667  }
668  if(parseReceivedHeader(payloadLength, 0) == 0) {
669  throw ProtocolException("Received header is too short!");
670  }
671  }
672  break;
673  case EOF_MESSAGE:
674  // This is the end of the frame
675  if(anyPayloadReceived()) {
676  parseEofMessage(length);
677  }
678  break;
679  case RESEND_MESSAGE: {
680  // The client requested retransmission of missing packets
681  parseResendMessage(payloadLength);
682  break;
683  }
684  case HEARTBEAT_MESSAGE:
685  // A cyclic heartbeat message
686  lastReceivedHeartbeat = std::chrono::steady_clock::now();
687  break;
688  default:
689  throw ProtocolException("Received invalid control message!");
690  break;
691  }
692 
693  return true;
694 }
695 
697  if(protType == PROTOCOL_TCP) {
698  // Connection is handled by TCP and not by us
699  return true;
700  } else if(connectionConfirmed) {
701  return !isServer || std::chrono::duration_cast<std::chrono::milliseconds>(
702  std::chrono::steady_clock::now() - lastReceivedHeartbeat).count()
703  < 2*HEARTBEAT_INTERVAL_MS;
704  } else return false;
705 }
706 
707 const unsigned char* DataBlockProtocol::getNextControlMessage(int& length) {
708  length = 0;
709 
710  if(protType == PROTOCOL_TCP) {
711  // There are no control messages for TCP
712  return nullptr;
713  }
714 
715  if(confirmationMessagePending) {
716  // Send confirmation message
717  confirmationMessagePending = false;
718  controlMessageBuffer[0] = CONFIRM_MESSAGE;
719  length = 1;
720  } else if(!isServer && std::chrono::duration_cast<std::chrono::milliseconds>(
721  std::chrono::steady_clock::now() - lastRemoteHostActivity).count() > RECONNECT_TIMEOUT_MS) {
722  // Send a new connection request
723  controlMessageBuffer[0] = CONNECTION_MESSAGE;
724  length = 1;
725 
726  // Also update time stamps
727  lastRemoteHostActivity = lastSentHeartbeat = std::chrono::steady_clock::now();
728  } else if(transferHeaderData != nullptr && isConnected()) {
729  // We need to send a new protocol header
730  length = transferHeaderSize;
731  const unsigned char* ret = transferHeaderData;
732  transferHeaderData = nullptr;
733  return ret;
734  } else if(eofMessagePending) {
735  // Send end of frame message
736  eofMessagePending = false;
737  unsigned int networkOffset = htonl(mergeRawOffset(lastTransmittedBlock, transferSize[lastTransmittedBlock]));
738  memcpy(&controlMessageBuffer[0], &networkOffset, sizeof(int));
739  controlMessageBuffer[sizeof(int)] = EOF_MESSAGE;
740  length = 5;
741  } else if(resendMessagePending) {
742  // Send a re-send request for missing messages
743  resendMessagePending = false;
744  if(!generateResendRequest(length)) {
745  length = 0;
746  return nullptr;
747  }
748  } else if(!isServer && std::chrono::duration_cast<std::chrono::milliseconds>(
749  std::chrono::steady_clock::now() - lastSentHeartbeat).count() > HEARTBEAT_INTERVAL_MS) {
750  // Send a heartbeat message
751  controlMessageBuffer[0] = HEARTBEAT_MESSAGE;
752  length = 1;
753  lastSentHeartbeat = std::chrono::steady_clock::now();
754  } else {
755  return nullptr;
756  }
757 
758  // Mark this message as a control message
759  controlMessageBuffer[length++] = 0xff;
760  controlMessageBuffer[length++] = 0xff;
761  controlMessageBuffer[length++] = 0xff;
762  controlMessageBuffer[length++] = 0xff;
763  return controlMessageBuffer;
764 }
765 
767  if(clientConnectionPending) {
768  clientConnectionPending = false;
769  return true;
770  } else {
771  return false;
772  }
773 }
774 
775 bool DataBlockProtocol::generateResendRequest(int& length) {
776  length = static_cast<int>(missingReceiveSegments.size() * (sizeof(int) + sizeof(unsigned short)));
777  if(length + sizeof(int) + 1> sizeof(controlMessageBuffer)) {
778  return false;
779  }
780 
781  length = 0;
782  for(MissingReceiveSegment segment: missingReceiveSegments) {
783  unsigned int segOffset = htonl(static_cast<unsigned int>(segment.offset));
784  unsigned int segLen = htonl(static_cast<unsigned int>(segment.length));
785 
786  memcpy(&controlMessageBuffer[length], &segOffset, sizeof(segOffset));
787  length += sizeof(unsigned int);
788  memcpy(&controlMessageBuffer[length], &segLen, sizeof(segLen));
789  length += sizeof(unsigned int);
790  }
791 
792  controlMessageBuffer[length++] = RESEND_MESSAGE;
793 
794  return true;
795 }
796 
797 void DataBlockProtocol::parseResendMessage(int length) {
798  missingTransferSegments.clear();
799 
800  int num = length / (sizeof(unsigned int) + sizeof(unsigned short));
801  int bufferOffset = 0;
802 
803  for(int i=0; i<num; i++) {
804  unsigned int segOffsetNet = *reinterpret_cast<unsigned int*>(&receiveBuffer[bufferOffset]);
805  bufferOffset += sizeof(unsigned int);
806  unsigned int segLenNet = *reinterpret_cast<unsigned int*>(&receiveBuffer[bufferOffset]);
807  bufferOffset += sizeof(unsigned int);
808 
809  int segmentOffsetRaw = static_cast<int>(ntohl(segOffsetNet)); // with block ID
810  int segmentLength = static_cast<int>(ntohl(segLenNet));
811  int dataBlockID, segmentOffset;
812  splitRawOffset(segmentOffsetRaw, dataBlockID, segmentOffset);
813 
814  if(segmentOffset >= 0 && segmentLength > 0 && (segmentOffset + segmentLength) <= rawValidBytes[dataBlockID]) {
815  missingTransferSegments.push_back(std::pair<int, int>(
816  segmentOffsetRaw, segmentLength));
817  }
818 
819  }
820 }
821 
822 void DataBlockProtocol::parseEofMessage(int length) {
823 
824  completedReceptions++;
825  lostSegmentRate = (lostSegmentRate * (completedReceptions-1) + ((double) lostSegmentBytes) / totalReceiveSize) / completedReceptions;
826 
827  if(length >= 4) {
828  // Find all missing segments at the end of blocks
829  for (int i=0; i<numReceptionBlocks; ++i) {
830  if (blockReceiveOffsets[i] < blockReceiveSize[i]) {
831  MissingReceiveSegment missingSeg;
832  missingSeg.offset = blockReceiveOffsets[i];
833  missingSeg.length = blockReceiveSize[i] - blockReceiveOffsets[i];
834  missingSeg.isEof = true;
835  missingReceiveSegments.push_back(missingSeg);
836  lostSegmentBytes += missingSeg.length;
837  }
838  }
839  if(missingReceiveSegments.size() > 0) {
840  waitingForMissingSegments = true;
841  resendMessagePending = true;
842  // Initialize all missing block start indices with earliest missing address
843  int mblock, moffset;
844  for (int i=0; i<static_cast<int>(missingReceiveSegments.size()); ++i) {
845  splitRawOffset(missingReceiveSegments[i].offset, mblock, moffset);
846  if (moffset < blockReceiveOffsets[mblock]) {
847  blockReceiveOffsets[mblock] = moffset;
848  }
849  }
850  } else {
851  finishedReception = true;
852  }
853  } else {
854  LOG_ERROR("EOF message too short, length " << length);
855  }
856 }
857 
858 void DataBlockProtocol::resizeReceiveBuffer() {
859  if(totalReceiveSize < 0) {
860  throw ProtocolException("Received invalid transfer size!");
861  }
862 
863  // We increase the requested size to allow for one
864  // additional network message and the protocol overhead
865  int bufferSize = 2*getMaxReceptionSize()
866  + MAX_OUTSTANDING_BYTES + sizeof(int);
867 
868  // Resize the buffer
869  if(static_cast<int>(receiveBuffer.size()) < bufferSize) {
870  receiveBuffer.resize(bufferSize);
871  }
872 
873  for (int i=0; i<numReceptionBlocks; ++i) {
874  if (static_cast<int>(blockReceiveBuffers[i].size()) < blockReceiveSize[i]) {
875  blockReceiveBuffers[i].resize(blockReceiveSize[i]);
876  }
877  }
878 }
879 
880 }} // namespace
881 
unsigned char * getNextReceiveBuffer(int maxLength)
Gets a buffer for receiving the next network message.
const unsigned char * getTransferMessage(int &length)
Gets the next network message for the current transfer.
const unsigned char * getNextControlMessage(int &length)
If a control message is pending to be transmitted, then the message data will be returned by this met...
void setTransferData(int block, unsigned char *data, int validBytes=0x7FFFFFFF)
Sets the payload data for the next transfer.
bool isConnected() const
Returns true if a remote connection is established.
void setTransferBytes(int block, long bytes)
Sets the per-block transfer size.
bool newClientConnected()
Returns true if the last network message has established a new connection from a client.
int getMaxReceptionSize() const
Returns the maximum payload size that can be received.
bool transferComplete()
Returns true if the current transfer has been completed.
void resetTransfer()
Resets all transfer related internal variables.
unsigned char * getReceivedData(int &length)
Returns the data that has been received for the current transfer.
void setTransferValidBytes(int block, int validBytes)
Updates the number of valid bytes in a partial transfer.
void setTransferHeader(unsigned char *data, int headerSize, int blocks)
Sets a user-defined header that shall be transmitted with the next transfer.
unsigned char * getReceivedHeader(int &length)
Returns the header data that has been received for the current transfer.
void processReceivedMessage(int length, bool &transferComplete)
Handles a received network message.
Exception class that is used for all protocol exceptions.
Definition: exceptions.h:25
void resetReception(bool dropped)
Resets the message reception.
Nerian Vision Technologies