otsdaq  3.10.00
TransceiverSocket.cc
1 #include "otsdaq/NetworkUtilities/TransceiverSocket.h"
2 #include "otsdaq/Macros/CoutMacros.h"
3 #include "otsdaq/MessageFacility/MessageFacility.h"
4 
5 #include <arpa/inet.h>
6 #include <unistd.h>
7 #include <chrono>
8 #include <cstring>
9 #include <iostream>
10 #include <limits>
11 #include <map>
12 #include <mutex>
13 #include <set>
14 #include <thread>
15 #include <vector>
16 
17 using namespace ots;
18 
19 //==============================================================================
20 TransceiverSocket::TransceiverSocket(void)
21 {
22  __COUT__ << "TransceiverSocket constructor " << __E__;
23 }
24 
25 //==============================================================================
26 TransceiverSocket::TransceiverSocket(std::string IPAddress, unsigned int port)
27  : Socket(IPAddress, port)
28 {
29  __COUT__ << "TransceiverSocket constructor " << IPAddress << ":" << port << __E__;
30 }
31 
32 //==============================================================================
33 TransceiverSocket::~TransceiverSocket(void) {}
34 
35 //==============================================================================
39 int TransceiverSocket::acknowledge(const std::string& buffer,
40  bool verbose /* = false */,
41  size_t maxChunkSize /* = 1500 */,
42  unsigned int interPacketGapUSeconds /* = 0 */,
43  bool enableRetransmission /* = false */)
44 {
45  if(verbose)
46  __COUTT__ << "Acknowledging on Socket Descriptor #: " << socketNumber_
47  << " from-port: " << ntohs(socketAddress_.sin_port)
48  << " to-port: " << ntohs(ReceiverSocket::fromAddress_.sin_port)
49  << " retransmission: " << (enableRetransmission ? "ON" : "OFF")
50  << std::endl;
51 
52  if(!enableRetransmission)
53  {
54  //====================================================================
55  // Original non-retransmission mode (unchanged behavior)
56  //====================================================================
57  // lockout other senders for the remainder of this scope
58  std::lock_guard<std::mutex> lock(sendMutex_);
59 
60  const size_t MAX_SEND_SIZE =
61  maxChunkSize > 65500u ? static_cast<size_t>(65500u) : maxChunkSize;
62  size_t offset = 0;
63  int sendToSize = 1;
64  int sizeInBytes = 1;
65 
66  while(offset < buffer.size() && sendToSize > 0)
67  {
68  auto thisSize = sizeInBytes * (buffer.size() - offset) > MAX_SEND_SIZE
69  ? MAX_SEND_SIZE
70  : sizeInBytes * (buffer.size() - offset);
71  if(verbose)
72  __COUTTV__(thisSize);
73  sendToSize = sendto(socketNumber_,
74  &buffer[0] + offset,
75  thisSize,
76  0,
77  (struct sockaddr*)&(ReceiverSocket::fromAddress_),
78  sizeof(sockaddr_in));
79  offset += sendToSize / sizeInBytes;
80  if(interPacketGapUSeconds > 0 && offset < buffer.size() && sendToSize > 0)
81  usleep(interPacketGapUSeconds);
82  }
83 
84  if(sendToSize <= 0)
85  {
86  __SS__ << "Error writing buffer from port "
87  << ntohs(TransmitterSocket::socketAddress_.sin_port) << ": "
88  << strerror(errno) << std::endl;
89  __SS_THROW__;
90  }
91  return 0;
92  }
93 
94  //====================================================================
95  // Retransmission mode: delegate entirely to sendAll() which handles
96  // packet building, initial send, and retransmit request handling.
97  //====================================================================
98  return sendAll(buffer, verbose, maxChunkSize, interPacketGapUSeconds);
99 } //end acknowledge()
100 
101 //==============================================================================
111 int TransceiverSocket::sendAll(const std::string& buffer,
112  bool verbose /* = false */,
113  size_t maxChunkSize /* = 65500 */,
114  unsigned int interPacketGapUSeconds /* = 0 */)
115 {
116  if(verbose)
117  __COUT__ << "sendAll: retransmission-mode send on Socket Descriptor #: "
118  << socketNumber_ << " from-port: " << ntohs(socketAddress_.sin_port)
119  << " to-port: " << ntohs(ReceiverSocket::fromAddress_.sin_port)
120  << " buffer size: " << buffer.size() << __E__;
121 
122  const size_t MAX_SEND_SIZE =
123  maxChunkSize > 65500u ? static_cast<size_t>(65500u) : maxChunkSize;
124 
125  // The payload per packet is reduced by the header size
126  const size_t payloadMax = MAX_SEND_SIZE > RETRANSMIT_HEADER_SIZE
127  ? MAX_SEND_SIZE - RETRANSMIT_HEADER_SIZE
128  : 1;
129 
130  // Calculate total number of packets. The on-wire header carries the count
131  // as uint16_t, so reject buffers that would require more than 65535 packets.
132  const size_t packetsNeeded = (buffer.size() + payloadMax - 1) / payloadMax;
133  if(packetsNeeded > std::numeric_limits<uint16_t>::max())
134  {
135  __SS__ << "sendAll: buffer size " << buffer.size() << " requires "
136  << packetsNeeded
137  << " packets, which exceeds the uint16_t protocol limit of "
138  << std::numeric_limits<uint16_t>::max() << " (payloadMax=" << payloadMax
139  << ")." << std::endl;
140  __SS_THROW__;
141  }
142  uint16_t totalPackets = static_cast<uint16_t>(packetsNeeded);
143  if(totalPackets == 0)
144  totalPackets = 1; // send at least one packet even for empty buffer
145 
146  if(verbose)
147  __COUT__ << "sendAll: sending " << totalPackets << " packets for "
148  << buffer.size() << " bytes, payloadMax=" << payloadMax << __E__;
149 
150  // Build and cache all packets (header + payload) for retransmit use
151  std::vector<std::string> packets(totalPackets);
152  {
153  size_t offset = 0;
154  for(uint16_t pi = 0; pi < totalPackets; ++pi)
155  {
156  size_t payloadSize = (buffer.size() - offset) > payloadMax
157  ? payloadMax
158  : (buffer.size() - offset);
159 
160  char header[RETRANSMIT_HEADER_SIZE];
161  uint16_t netMagic = htons(RETRANSMIT_MAGIC);
162  uint16_t netIndex = htons(pi);
163  uint16_t netTotal = htons(totalPackets);
164  uint16_t netPaySize = htons(static_cast<uint16_t>(payloadSize));
165  std::memcpy(header + 0, &netMagic, 2);
166  std::memcpy(header + 2, &netIndex, 2);
167  std::memcpy(header + 4, &netTotal, 2);
168  std::memcpy(header + 6, &netPaySize, 2);
169 
170  packets[pi].assign(header, RETRANSMIT_HEADER_SIZE);
171  packets[pi].append(buffer, offset, payloadSize);
172  offset += payloadSize;
173  }
174  }
175 
176  // Send all packets initially (lock sendMutex_ for the burst)
177  {
178  std::lock_guard<std::mutex> lock(sendMutex_);
179  for(uint16_t pi = 0; pi < totalPackets; ++pi)
180  {
181  int sendToSize = sendto(socketNumber_,
182  packets[pi].data(),
183  packets[pi].size(),
184  0,
185  (struct sockaddr*)&(ReceiverSocket::fromAddress_),
186  sizeof(sockaddr_in));
187  if(sendToSize <= 0)
188  {
189  __SS__ << "sendAll: error writing packet " << pi << "/" << totalPackets
190  << " from port " << ntohs(socketAddress_.sin_port) << ": "
191  << strerror(errno) << std::endl;
192  __SS_THROW__;
193  }
194  if(verbose)
195  __COUTT__ << "sendAll: sent packet " << pi << "/" << totalPackets
196  << " size=" << packets[pi].size() << std::endl;
197 
198  if(interPacketGapUSeconds > 0 && pi + 1 < totalPackets)
199  usleep(interPacketGapUSeconds);
200  }
201  }
202 
203  // Wait for retransmit requests from receiver.
204  // Retransmit request format: magic(2 bytes) + list of uint16 missing indices
205  // Done signal format: magic(2 bytes) + 0xFFFF(2 bytes)
206  const unsigned int retransmitTimeoutSeconds = 5;
207  const unsigned int maxRetransmitRounds = 20;
208 
209  for(unsigned int round = 0; round < maxRetransmitRounds; ++round)
210  {
211  std::string retransmitRequest;
212  int rc = receive(retransmitRequest,
213  retransmitTimeoutSeconds,
214  0 /*timeoutUSeconds*/,
215  false /*verbose*/);
216  if(rc < 0)
217  {
218  // Timeout - assume receiver got everything (or gave up)
219  if(verbose)
220  __COUT__ << "sendAll: no retransmit request after "
221  << retransmitTimeoutSeconds
222  << "s timeout, assuming transfer complete." << __E__;
223  break;
224  }
225 
226  if(retransmitRequest.size() < 4)
227  continue;
228 
229  uint16_t reqMagic;
230  std::memcpy(&reqMagic, retransmitRequest.data(), 2);
231  reqMagic = ntohs(reqMagic);
232  if(reqMagic != RETRANSMIT_MAGIC)
233  continue;
234 
235  // Check for "done" signal (magic + 0xFFFF)
236  uint16_t firstVal;
237  std::memcpy(&firstVal, retransmitRequest.data() + 2, 2);
238  firstVal = ntohs(firstVal);
239  if(firstVal == 0xFFFF)
240  {
241  if(verbose)
242  __COUT__ << "sendAll: received 'all done' from receiver." << __E__;
243  break;
244  }
245 
246  // Parse list of missing packet indices and resend them
247  size_t numIndices = (retransmitRequest.size() - 2) / 2;
248  if(verbose)
249  __COUT__ << "sendAll: retransmit request for " << numIndices
250  << " packets (round " << round << ")." << __E__;
251 
252  // Lock sendMutex_ for the resend burst
253  std::lock_guard<std::mutex> lock(sendMutex_);
254  for(size_t i = 0; i < numIndices; ++i)
255  {
256  uint16_t missingIdx;
257  std::memcpy(&missingIdx, retransmitRequest.data() + 2 + i * 2, 2);
258  missingIdx = ntohs(missingIdx);
259 
260  if(missingIdx < totalPackets)
261  {
262  int sendToSize = sendto(socketNumber_,
263  packets[missingIdx].data(),
264  packets[missingIdx].size(),
265  0,
266  (struct sockaddr*)&(ReceiverSocket::fromAddress_),
267  sizeof(sockaddr_in));
268  if(sendToSize <= 0)
269  {
270  __SS__ << "sendAll: error resending packet " << missingIdx << ": "
271  << strerror(errno) << std::endl;
272  __SS_THROW__;
273  }
274  if(verbose)
275  __COUTT__ << "sendAll: resent packet " << missingIdx << std::endl;
276 
277  if(interPacketGapUSeconds > 0)
278  usleep(interPacketGapUSeconds);
279  }
280  else
281  {
282  __COUT_WARN__ << "sendAll: retransmit request for invalid packet index "
283  << missingIdx << " (total=" << totalPackets << ")" << __E__;
284  }
285  }
286  }
287 
288  return 0;
289 } //end sendAll()
290 
291 //==============================================================================
296  Socket& toSocket,
297  const std::string& sendBuffer,
298  unsigned int timeoutSeconds /* = 1 */,
299  unsigned int timeoutUSeconds /* = 0 */,
300  bool verbose /* = false */,
301  unsigned int interPacketTimeoutUSeconds /* = 10000 */)
302 {
303  using clock = std::chrono::steady_clock;
304  auto start = clock::now();
305 
306  // lockout other sender and receive attempts for the remainder of the scope
307  std::lock_guard<std::mutex> lock(
308  sendAndReceiveMutex_); //note that TransmitterSocket::sendMutex_ is not enough
309 
310  flush(); //make sure nothing to read before sending
311 
312  send(toSocket, sendBuffer, verbose);
313 
314  __COUTT__ << " ----> Time sendAndReceive '" << sendBuffer
315  << "' (socketNumber=" << socketNumber_ << ") check ==> "
316  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
317  start)
318  .count()
319  << " milliseconds. PID=" << getpid()
320  << " TID=" << std::this_thread::get_id() << std::endl;
321 
322  std::string receiveBuffer;
323  if(receive(receiveBuffer, timeoutSeconds, timeoutUSeconds, verbose) < 0)
324  {
325  __SS__ << "Timeout (" << timeoutSeconds + timeoutUSeconds / 1000000.
326  << " s) or Error receiving response buffer from remote ip:port "
327  << toSocket.getIPAddress() << ":" << toSocket.getPort()
328  << " to this ip:port " << Socket::getIPAddress() << ":"
329  << Socket::getPort() << __E__;
330  __SS_ONLY_THROW__;
331  }
332  __COUTT__ << " ----> Time sendAndReceive '" << sendBuffer << "' got "
333  << receiveBuffer.size() << " (socketNumber=" << socketNumber_
334  << ") check ==> "
335  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
336  start)
337  .count()
338  << " milliseconds. PID=" << getpid()
339  << " TID=" << std::this_thread::get_id() << std::endl;
340 
341  //assume response may be multiple packets! (and give interPacketTimeoutUSeconds unless called with lower timeout)
342  size_t extraPackets = 0;
343  std::string receiveBuffer2;
344  while(receive(receiveBuffer2,
345  0 /*timeoutSeconds*/,
346  (timeoutSeconds == 0 && timeoutUSeconds < interPacketTimeoutUSeconds)
347  ? timeoutUSeconds
348  : interPacketTimeoutUSeconds,
349  verbose) >= 0)
350  {
351  ++extraPackets;
352  receiveBuffer += receiveBuffer2; //append
353 
354  __COUTT__ << " ----> Time sendAndReceive +" << receiveBuffer2.size()
355  << " check ==> "
356  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
357  start)
358  .count()
359  << " milliseconds." << std::endl;
360  }
361  __COUTT__ << " ----> Time sendAndReceive " << receiveBuffer.size() << " check ==> "
362  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
363  start)
364  .count()
365  << " milliseconds." << std::endl;
366 
367  return receiveBuffer;
368 } //end sendAndReceive()
369 
370 //==============================================================================
385 int TransceiverSocket::receiveAll(std::string& buffer,
386  unsigned int timeoutSeconds /* = 5 */,
387  unsigned int retransmitMaxRetries /* = 10 */,
388  bool verbose /* = false */)
389 {
390  using clock = std::chrono::steady_clock;
391  auto start = clock::now();
392 
393  // Map of packet index -> payload data
394  std::map<uint16_t, std::string> receivedPackets;
395  uint16_t totalPackets = 0;
396  bool totalKnown = false;
397 
398  if(verbose)
399  __COUT__ << "receiveAll: waiting for retransmission-mode packets, timeout="
400  << timeoutSeconds << "s" << __E__;
401 
402  // Phase 1: Receive all initial packets until timeout
403  // Use a per-packet timeout that is shorter than the overall timeout,
404  // so we can detect "no more packets arriving" vs "still waiting for first"
405  const unsigned int interPacketTimeoutUSeconds = 100000; // 100ms between packets
406  bool firstPacketReceived = false;
407 
408  while(true)
409  {
410  std::string rawPacket;
411  int rc = receive(rawPacket,
412  firstPacketReceived ? 0 : timeoutSeconds,
413  firstPacketReceived ? interPacketTimeoutUSeconds : 0,
414  false /*verbose*/);
415 
416  if(rc < 0)
417  {
418  if(!firstPacketReceived)
419  {
420  // Never received any packet at all
421  if(verbose)
422  __COUT__ << "receiveAll: timeout waiting for first packet after "
423  << timeoutSeconds << "s" << __E__;
424  return -1;
425  }
426  // Timeout between packets - move to retransmit phase
427  break;
428  }
429 
430  // Check for retransmission header
431  if(rawPacket.size() < RETRANSMIT_HEADER_SIZE)
432  {
433  // Too small to be a retransmission packet - might be a non-retransmit
434  // response; just return it as-is
435  if(!firstPacketReceived)
436  {
437  buffer = rawPacket;
438  return 0;
439  }
440  // Skip malformed packet during multi-packet receive
441  if(verbose)
442  __COUT_WARN__ << "receiveAll: skipping undersized packet ("
443  << rawPacket.size() << " bytes)" << __E__;
444  continue;
445  }
446 
447  // Parse header
448  uint16_t magic, packetIndex, pktTotal, payloadSize;
449  std::memcpy(&magic, rawPacket.data() + 0, 2);
450  std::memcpy(&packetIndex, rawPacket.data() + 2, 2);
451  std::memcpy(&pktTotal, rawPacket.data() + 4, 2);
452  std::memcpy(&payloadSize, rawPacket.data() + 6, 2);
453  magic = ntohs(magic);
454  packetIndex = ntohs(packetIndex);
455  pktTotal = ntohs(pktTotal);
456  payloadSize = ntohs(payloadSize);
457 
458  if(magic != RETRANSMIT_MAGIC)
459  {
460  // Not a retransmission packet - if first packet, return as-is
461  if(!firstPacketReceived)
462  {
463  buffer = rawPacket;
464  return 0;
465  }
466  if(verbose)
467  __COUT_WARN__ << "receiveAll: skipping packet with bad magic 0x"
468  << std::hex << magic << std::dec << __E__;
469  continue;
470  }
471 
472  firstPacketReceived = true;
473  totalPackets = pktTotal;
474  totalKnown = true;
475 
476  // Extract payload (everything after the 8-byte header, limited by payloadSize)
477  size_t actualPayload = rawPacket.size() - RETRANSMIT_HEADER_SIZE;
478  if(actualPayload > payloadSize)
479  actualPayload = payloadSize;
480 
481  receivedPackets[packetIndex] =
482  rawPacket.substr(RETRANSMIT_HEADER_SIZE, actualPayload);
483 
484  if(verbose)
485  __COUTT__ << "receiveAll: received packet " << packetIndex << "/"
486  << totalPackets << " payload=" << actualPayload
487  << " total_received=" << receivedPackets.size() << std::endl;
488 
489  // Check if we have all packets
490  if(totalKnown && receivedPackets.size() >= static_cast<size_t>(totalPackets))
491  break;
492 
493  // Check overall timeout
494  auto elapsed =
495  std::chrono::duration_cast<std::chrono::seconds>(clock::now() - start);
496  if(elapsed.count() >=
497  static_cast<long>(timeoutSeconds * (retransmitMaxRetries + 1)))
498  {
499  if(verbose)
500  __COUT_WARN__ << "receiveAll: overall timeout reached" << __E__;
501  break;
502  }
503  }
504 
505  // Phase 2: Retransmit missing packets
506  if(totalKnown && receivedPackets.size() < static_cast<size_t>(totalPackets))
507  {
508  for(unsigned int retry = 0; retry < retransmitMaxRetries; ++retry)
509  {
510  // Build list of missing packet indices
511  std::set<uint16_t> missing;
512  for(uint16_t i = 0; i < totalPackets; ++i)
513  {
514  if(receivedPackets.find(i) == receivedPackets.end())
515  missing.insert(i);
516  }
517 
518  if(missing.empty())
519  break;
520 
521  if(verbose)
522  __COUT__ << "receiveAll: retry " << retry + 1 << "/"
523  << retransmitMaxRetries << ", requesting retransmit of "
524  << missing.size() << " packets" << __E__;
525 
526  // Build retransmit request: magic(2 bytes) + list of uint16 indices
527  std::string retransmitReq;
528  retransmitReq.resize(2 + missing.size() * 2);
529  uint16_t netMagic = htons(RETRANSMIT_MAGIC);
530  std::memcpy(&retransmitReq[0], &netMagic, 2);
531  size_t pos = 2;
532  for(uint16_t idx : missing)
533  {
534  uint16_t netIdx = htons(idx);
535  std::memcpy(&retransmitReq[pos], &netIdx, 2);
536  pos += 2;
537  }
538 
539  // Send retransmit request back to sender (acknowledge to last receive addr)
540  {
541  // Use sendto directly to fromAddress_ (the sender)
542  int sendToSize = sendto(socketNumber_,
543  retransmitReq.data(),
544  retransmitReq.size(),
545  0,
546  (struct sockaddr*)&(ReceiverSocket::fromAddress_),
547  sizeof(sockaddr_in));
548  if(sendToSize <= 0)
549  {
550  __COUT_WARN__ << "receiveAll: failed to send retransmit request: "
551  << strerror(errno) << __E__;
552  }
553  }
554 
555  // Receive retransmitted packets
556  while(true)
557  {
558  std::string rawPacket;
559  int rc = receive(
560  rawPacket, timeoutSeconds, 0 /*timeoutUSeconds*/, false /*verbose*/);
561  if(rc < 0)
562  break; // timeout, will retry
563 
564  if(rawPacket.size() < RETRANSMIT_HEADER_SIZE)
565  continue;
566 
567  uint16_t magic2, packetIndex2, pktTotal2, payloadSize2;
568  std::memcpy(&magic2, rawPacket.data() + 0, 2);
569  std::memcpy(&packetIndex2, rawPacket.data() + 2, 2);
570  std::memcpy(&pktTotal2, rawPacket.data() + 4, 2);
571  std::memcpy(&payloadSize2, rawPacket.data() + 6, 2);
572  magic2 = ntohs(magic2);
573  packetIndex2 = ntohs(packetIndex2);
574  pktTotal2 = ntohs(pktTotal2);
575  payloadSize2 = ntohs(payloadSize2);
576 
577  if(magic2 != RETRANSMIT_MAGIC)
578  continue;
579 
580  size_t actualPayload2 = rawPacket.size() - RETRANSMIT_HEADER_SIZE;
581  if(actualPayload2 > payloadSize2)
582  actualPayload2 = payloadSize2;
583 
584  receivedPackets[packetIndex2] =
585  rawPacket.substr(RETRANSMIT_HEADER_SIZE, actualPayload2);
586 
587  if(verbose)
588  __COUTT__ << "receiveAll: retransmit received packet " << packetIndex2
589  << "/" << totalPackets
590  << " total_received=" << receivedPackets.size()
591  << std::endl;
592 
593  // Check if we now have all packets
594  if(receivedPackets.size() >= static_cast<size_t>(totalPackets))
595  break;
596  }
597 
598  if(receivedPackets.size() >= static_cast<size_t>(totalPackets))
599  break;
600  }
601  }
602 
603  // Phase 3: Send "done" acknowledgment to sender (magic + 0xFFFF)
604  {
605  std::string doneSignal(4, '\0');
606  uint16_t netMagic = htons(RETRANSMIT_MAGIC);
607  uint16_t netDone = htons(0xFFFF);
608  std::memcpy(&doneSignal[0], &netMagic, 2);
609  std::memcpy(&doneSignal[2], &netDone, 2);
610  sendto(socketNumber_,
611  doneSignal.data(),
612  doneSignal.size(),
613  0,
614  (struct sockaddr*)&(ReceiverSocket::fromAddress_),
615  sizeof(sockaddr_in));
616  }
617 
618  // Phase 4: Assemble full buffer in order
619  if(!totalKnown || receivedPackets.empty())
620  {
621  __SS__ << "receiveAll: failed to receive any retransmission-mode packets"
622  << __E__;
623  __SS_THROW__;
624  }
625 
626  if(receivedPackets.size() < static_cast<size_t>(totalPackets))
627  {
628  // Build list of still-missing indices for the error message
629  std::string missingStr;
630  for(uint16_t i = 0; i < totalPackets; ++i)
631  {
632  if(receivedPackets.find(i) == receivedPackets.end())
633  {
634  if(!missingStr.empty())
635  missingStr += ", ";
636  missingStr += std::to_string(i);
637  }
638  }
639  __SS__ << "receiveAll: failed to receive all packets after "
640  << retransmitMaxRetries << " retransmit retries. "
641  << "Received " << receivedPackets.size() << "/" << totalPackets
642  << " packets. Missing indices: [" << missingStr << "]" << __E__;
643  __SS_THROW__;
644  }
645 
646  // Assemble in order
647  buffer.clear();
648  for(uint16_t i = 0; i < totalPackets; ++i)
649  buffer += receivedPackets[i];
650 
651  if(verbose)
652  __COUT__ << "receiveAll: successfully assembled " << buffer.size()
653  << " bytes from " << totalPackets << " packets in "
654  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
655  start)
656  .count()
657  << " ms" << __E__;
658 
659  return 0;
660 } //end receiveAll()
661 
662 //==============================================================================
676  Socket& toSocket,
677  const std::string& sendBuffer,
678  unsigned int timeoutSeconds /* = 5 */,
679  unsigned int retransmitMaxRetries /* = 10 */,
680  bool verbose /* = false */)
681 {
682  using clock = std::chrono::steady_clock;
683  auto start = clock::now();
684 
685  // lockout other sender and receive attempts for the remainder of the scope
686  std::lock_guard<std::mutex> lock(sendAndReceiveMutex_);
687 
688  flush(); // make sure nothing to read before sending
689  send(toSocket, sendBuffer, verbose);
690 
691  __COUTT__ << " ----> Time sendAndReceiveAll '" << sendBuffer
692  << "' (socketNumber=" << socketNumber_ << ") check ==> "
693  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
694  start)
695  .count()
696  << " milliseconds. PID=" << getpid()
697  << " TID=" << std::this_thread::get_id() << std::endl;
698 
699  std::string receiveBuffer;
700  if(receiveAll(receiveBuffer, timeoutSeconds, retransmitMaxRetries, verbose) < 0)
701  {
702  __SS__ << "Timeout (" << timeoutSeconds
703  << " s) or Error receiving retransmission response from remote ip:port "
704  << toSocket.getIPAddress() << ":" << toSocket.getPort()
705  << " to this ip:port " << Socket::getIPAddress() << ":"
706  << Socket::getPort() << __E__;
707  __SS_ONLY_THROW__;
708  }
709 
710  __COUTT__ << " ----> Time sendAndReceiveAll complete: " << receiveBuffer.size()
711  << " bytes (socketNumber=" << socketNumber_ << ") ==> "
712  << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
713  start)
714  .count()
715  << " milliseconds. PID=" << getpid()
716  << " TID=" << std::this_thread::get_id() << std::endl;
717 
718  return receiveBuffer;
719 } //end sendAndReceiveAll()
int receive(std::string &buffer, unsigned int timeoutSeconds=1, unsigned int timeoutUSeconds=0, bool verbose=false)
returns count of dropped packets
int acknowledge(const std::string &buffer, bool verbose=false, size_t maxChunkSize=1500, unsigned int interPacketGapUSeconds=0, bool enableRetransmission=false)
std::string sendAndReceiveAll(Socket &toSocket, const std::string &sendBuffer, unsigned int timeoutSeconds=5, unsigned int retransmitMaxRetries=10, bool verbose=false)
static constexpr uint16_t RETRANSMIT_MAGIC
Retransmission protocol constants.
int sendAll(const std::string &buffer, bool verbose=false, size_t maxChunkSize=65500, unsigned int interPacketGapUSeconds=0)
std::string sendAndReceive(Socket &toSocket, const std::string &sendBuffer, unsigned int timeoutSeconds=1, unsigned int timeoutUSeconds=0, bool verbose=false, unsigned int interPacketTimeoutUSeconds=10000)
int receiveAll(std::string &buffer, unsigned int timeoutSeconds=5, unsigned int retransmitMaxRetries=10, bool verbose=false)
defines used also by OtsConfigurationWizardSupervisor