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