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