1 #include "otsdaq/NetworkUtilities/TransceiverSocket.h"
2 #include "otsdaq/Macros/CoutMacros.h"
3 #include "otsdaq/MessageFacility/MessageFacility.h"
20 TransceiverSocket::TransceiverSocket(
void)
22 __COUT__ <<
"TransceiverSocket constructor " << __E__;
26 TransceiverSocket::TransceiverSocket(std::string IPAddress,
unsigned int port)
29 __COUT__ <<
"TransceiverSocket constructor " << IPAddress <<
":" << port << __E__;
33 TransceiverSocket::~TransceiverSocket(
void) {}
42 unsigned int interPacketGapUSeconds ,
43 bool enableRetransmission )
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")
52 if(!enableRetransmission)
58 std::lock_guard<std::mutex> lock(sendMutex_);
60 const size_t MAX_SEND_SIZE =
61 maxChunkSize > 65500u ?
static_cast<size_t>(65500u) : maxChunkSize;
66 while(offset < buffer.size() && sendToSize > 0)
68 auto thisSize = sizeInBytes * (buffer.size() - offset) > MAX_SEND_SIZE
70 : sizeInBytes * (buffer.size() - offset);
73 sendToSize = sendto(socketNumber_,
77 (
struct sockaddr*)&(ReceiverSocket::fromAddress_),
79 offset += sendToSize / sizeInBytes;
80 if(interPacketGapUSeconds > 0 && offset < buffer.size() && sendToSize > 0)
81 usleep(interPacketGapUSeconds);
86 __SS__ <<
"Error writing buffer from port "
87 << ntohs(TransmitterSocket::socketAddress_.sin_port) <<
": "
88 << strerror(errno) << std::endl;
98 return sendAll(buffer, verbose, maxChunkSize, interPacketGapUSeconds);
113 size_t maxChunkSize ,
114 unsigned int interPacketGapUSeconds )
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__;
122 const size_t MAX_SEND_SIZE =
123 maxChunkSize > 65500u ?
static_cast<size_t>(65500u) : maxChunkSize;
126 const size_t payloadMax = MAX_SEND_SIZE > RETRANSMIT_HEADER_SIZE
127 ? MAX_SEND_SIZE - RETRANSMIT_HEADER_SIZE
132 const size_t packetsNeeded = (buffer.size() + payloadMax - 1) / payloadMax;
133 if(packetsNeeded > std::numeric_limits<uint16_t>::max())
135 __SS__ <<
"sendAll: buffer size " << buffer.size() <<
" requires "
137 <<
" packets, which exceeds the uint16_t protocol limit of "
138 << std::numeric_limits<uint16_t>::max() <<
" (payloadMax=" << payloadMax
139 <<
")." << std::endl;
142 uint16_t totalPackets =
static_cast<uint16_t
>(packetsNeeded);
143 if(totalPackets == 0)
147 __COUT__ <<
"sendAll: sending " << totalPackets <<
" packets for "
148 << buffer.size() <<
" bytes, payloadMax=" << payloadMax << __E__;
151 std::vector<std::string> packets(totalPackets);
154 for(uint16_t pi = 0; pi < totalPackets; ++pi)
156 size_t payloadSize = (buffer.size() - offset) > payloadMax
158 : (buffer.size() - offset);
160 char header[RETRANSMIT_HEADER_SIZE];
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);
170 packets[pi].assign(header, RETRANSMIT_HEADER_SIZE);
171 packets[pi].append(buffer, offset, payloadSize);
172 offset += payloadSize;
178 std::lock_guard<std::mutex> lock(sendMutex_);
179 for(uint16_t pi = 0; pi < totalPackets; ++pi)
181 int sendToSize = sendto(socketNumber_,
185 (
struct sockaddr*)&(ReceiverSocket::fromAddress_),
186 sizeof(sockaddr_in));
189 __SS__ <<
"sendAll: error writing packet " << pi <<
"/" << totalPackets
190 <<
" from port " << ntohs(socketAddress_.sin_port) <<
": "
191 << strerror(errno) << std::endl;
195 __COUTT__ <<
"sendAll: sent packet " << pi <<
"/" << totalPackets
196 <<
" size=" << packets[pi].size() << std::endl;
198 if(interPacketGapUSeconds > 0 && pi + 1 < totalPackets)
199 usleep(interPacketGapUSeconds);
206 const unsigned int retransmitTimeoutSeconds = 5;
207 const unsigned int maxRetransmitRounds = 20;
209 for(
unsigned int round = 0; round < maxRetransmitRounds; ++round)
211 std::string retransmitRequest;
212 int rc =
receive(retransmitRequest,
213 retransmitTimeoutSeconds,
220 __COUT__ <<
"sendAll: no retransmit request after "
221 << retransmitTimeoutSeconds
222 <<
"s timeout, assuming transfer complete." << __E__;
226 if(retransmitRequest.size() < 4)
230 std::memcpy(&reqMagic, retransmitRequest.data(), 2);
231 reqMagic = ntohs(reqMagic);
237 std::memcpy(&firstVal, retransmitRequest.data() + 2, 2);
238 firstVal = ntohs(firstVal);
239 if(firstVal == 0xFFFF)
242 __COUT__ <<
"sendAll: received 'all done' from receiver." << __E__;
247 size_t numIndices = (retransmitRequest.size() - 2) / 2;
249 __COUT__ <<
"sendAll: retransmit request for " << numIndices
250 <<
" packets (round " << round <<
")." << __E__;
253 std::lock_guard<std::mutex> lock(sendMutex_);
254 for(
size_t i = 0; i < numIndices; ++i)
257 std::memcpy(&missingIdx, retransmitRequest.data() + 2 + i * 2, 2);
258 missingIdx = ntohs(missingIdx);
260 if(missingIdx < totalPackets)
262 int sendToSize = sendto(socketNumber_,
263 packets[missingIdx].data(),
264 packets[missingIdx].size(),
266 (
struct sockaddr*)&(ReceiverSocket::fromAddress_),
267 sizeof(sockaddr_in));
270 __SS__ <<
"sendAll: error resending packet " << missingIdx <<
": "
271 << strerror(errno) << std::endl;
275 __COUTT__ <<
"sendAll: resent packet " << missingIdx << std::endl;
277 if(interPacketGapUSeconds > 0)
278 usleep(interPacketGapUSeconds);
282 __COUT_WARN__ <<
"sendAll: retransmit request for invalid packet index "
283 << missingIdx <<
" (total=" << totalPackets <<
")" << __E__;
297 const std::string& sendBuffer,
298 unsigned int timeoutSeconds ,
299 unsigned int timeoutUSeconds ,
301 unsigned int interPacketTimeoutUSeconds )
303 using clock = std::chrono::steady_clock;
304 auto start = clock::now();
307 std::lock_guard<std::mutex> lock(
308 sendAndReceiveMutex_);
312 send(toSocket, sendBuffer, verbose);
314 __COUTT__ <<
" ----> Time sendAndReceive '" << sendBuffer
315 <<
"' (socketNumber=" << socketNumber_ <<
") check ==> "
316 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
319 <<
" milliseconds. PID=" << getpid()
320 <<
" TID=" << std::this_thread::get_id() << std::endl;
322 std::string receiveBuffer;
323 if(
receive(receiveBuffer, timeoutSeconds, timeoutUSeconds, verbose) < 0)
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__;
332 __COUTT__ <<
" ----> Time sendAndReceive '" << sendBuffer <<
"' got "
333 << receiveBuffer.size() <<
" (socketNumber=" << socketNumber_
335 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
338 <<
" milliseconds. PID=" << getpid()
339 <<
" TID=" << std::this_thread::get_id() << std::endl;
342 size_t extraPackets = 0;
343 std::string receiveBuffer2;
346 (timeoutSeconds == 0 && timeoutUSeconds < interPacketTimeoutUSeconds)
348 : interPacketTimeoutUSeconds,
352 receiveBuffer += receiveBuffer2;
354 __COUTT__ <<
" ----> Time sendAndReceive +" << receiveBuffer2.size()
356 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
359 <<
" milliseconds." << std::endl;
361 __COUTT__ <<
" ----> Time sendAndReceive " << receiveBuffer.size() <<
" check ==> "
362 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
365 <<
" milliseconds." << std::endl;
367 return receiveBuffer;
386 unsigned int timeoutSeconds ,
387 unsigned int retransmitMaxRetries ,
390 using clock = std::chrono::steady_clock;
391 auto start = clock::now();
394 std::map<uint16_t, std::string> receivedPackets;
395 uint16_t totalPackets = 0;
396 bool totalKnown =
false;
399 __COUT__ <<
"receiveAll: waiting for retransmission-mode packets, timeout="
400 << timeoutSeconds <<
"s" << __E__;
405 const unsigned int interPacketTimeoutUSeconds = 100000;
406 bool firstPacketReceived =
false;
410 std::string rawPacket;
412 firstPacketReceived ? 0 : timeoutSeconds,
413 firstPacketReceived ? interPacketTimeoutUSeconds : 0,
418 if(!firstPacketReceived)
422 __COUT__ <<
"receiveAll: timeout waiting for first packet after "
423 << timeoutSeconds <<
"s" << __E__;
431 if(rawPacket.size() < RETRANSMIT_HEADER_SIZE)
435 if(!firstPacketReceived)
442 __COUT_WARN__ <<
"receiveAll: skipping undersized packet ("
443 << rawPacket.size() <<
" bytes)" << __E__;
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);
461 if(!firstPacketReceived)
467 __COUT_WARN__ <<
"receiveAll: skipping packet with bad magic 0x"
468 << std::hex << magic << std::dec << __E__;
472 firstPacketReceived =
true;
473 totalPackets = pktTotal;
477 size_t actualPayload = rawPacket.size() - RETRANSMIT_HEADER_SIZE;
478 if(actualPayload > payloadSize)
479 actualPayload = payloadSize;
481 receivedPackets[packetIndex] =
482 rawPacket.substr(RETRANSMIT_HEADER_SIZE, actualPayload);
485 __COUTT__ <<
"receiveAll: received packet " << packetIndex <<
"/"
486 << totalPackets <<
" payload=" << actualPayload
487 <<
" total_received=" << receivedPackets.size() << std::endl;
490 if(totalKnown && receivedPackets.size() >=
static_cast<size_t>(totalPackets))
495 std::chrono::duration_cast<std::chrono::seconds>(clock::now() - start);
496 if(elapsed.count() >=
497 static_cast<long>(timeoutSeconds * (retransmitMaxRetries + 1)))
500 __COUT_WARN__ <<
"receiveAll: overall timeout reached" << __E__;
506 if(totalKnown && receivedPackets.size() <
static_cast<size_t>(totalPackets))
508 for(
unsigned int retry = 0; retry < retransmitMaxRetries; ++retry)
511 std::set<uint16_t> missing;
512 for(uint16_t i = 0; i < totalPackets; ++i)
514 if(receivedPackets.find(i) == receivedPackets.end())
522 __COUT__ <<
"receiveAll: retry " << retry + 1 <<
"/"
523 << retransmitMaxRetries <<
", requesting retransmit of "
524 << missing.size() <<
" packets" << __E__;
527 std::string retransmitReq;
528 retransmitReq.resize(2 + missing.size() * 2);
530 std::memcpy(&retransmitReq[0], &netMagic, 2);
532 for(uint16_t idx : missing)
534 uint16_t netIdx = htons(idx);
535 std::memcpy(&retransmitReq[pos], &netIdx, 2);
542 int sendToSize = sendto(socketNumber_,
543 retransmitReq.data(),
544 retransmitReq.size(),
546 (
struct sockaddr*)&(ReceiverSocket::fromAddress_),
547 sizeof(sockaddr_in));
550 __COUT_WARN__ <<
"receiveAll: failed to send retransmit request: "
551 << strerror(errno) << __E__;
558 std::string rawPacket;
560 rawPacket, timeoutSeconds, 0 ,
false );
564 if(rawPacket.size() < RETRANSMIT_HEADER_SIZE)
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);
580 size_t actualPayload2 = rawPacket.size() - RETRANSMIT_HEADER_SIZE;
581 if(actualPayload2 > payloadSize2)
582 actualPayload2 = payloadSize2;
584 receivedPackets[packetIndex2] =
585 rawPacket.substr(RETRANSMIT_HEADER_SIZE, actualPayload2);
588 __COUTT__ <<
"receiveAll: retransmit received packet " << packetIndex2
589 <<
"/" << totalPackets
590 <<
" total_received=" << receivedPackets.size()
594 if(receivedPackets.size() >=
static_cast<size_t>(totalPackets))
598 if(receivedPackets.size() >=
static_cast<size_t>(totalPackets))
605 std::string doneSignal(4,
'\0');
607 uint16_t netDone = htons(0xFFFF);
608 std::memcpy(&doneSignal[0], &netMagic, 2);
609 std::memcpy(&doneSignal[2], &netDone, 2);
610 sendto(socketNumber_,
614 (
struct sockaddr*)&(ReceiverSocket::fromAddress_),
615 sizeof(sockaddr_in));
619 if(!totalKnown || receivedPackets.empty())
621 __SS__ <<
"receiveAll: failed to receive any retransmission-mode packets"
626 if(receivedPackets.size() <
static_cast<size_t>(totalPackets))
629 std::string missingStr;
630 for(uint16_t i = 0; i < totalPackets; ++i)
632 if(receivedPackets.find(i) == receivedPackets.end())
634 if(!missingStr.empty())
636 missingStr += std::to_string(i);
639 __SS__ <<
"receiveAll: failed to receive all packets after "
640 << retransmitMaxRetries <<
" retransmit retries. "
641 <<
"Received " << receivedPackets.size() <<
"/" << totalPackets
642 <<
" packets. Missing indices: [" << missingStr <<
"]" << __E__;
648 for(uint16_t i = 0; i < totalPackets; ++i)
649 buffer += receivedPackets[i];
652 __COUT__ <<
"receiveAll: successfully assembled " << buffer.size()
653 <<
" bytes from " << totalPackets <<
" packets in "
654 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
677 const std::string& sendBuffer,
678 unsigned int timeoutSeconds ,
679 unsigned int retransmitMaxRetries ,
682 using clock = std::chrono::steady_clock;
683 auto start = clock::now();
686 std::lock_guard<std::mutex> lock(sendAndReceiveMutex_);
689 send(toSocket, sendBuffer, verbose);
691 __COUTT__ <<
" ----> Time sendAndReceiveAll '" << sendBuffer
692 <<
"' (socketNumber=" << socketNumber_ <<
") check ==> "
693 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
696 <<
" milliseconds. PID=" << getpid()
697 <<
" TID=" << std::this_thread::get_id() << std::endl;
699 std::string receiveBuffer;
700 if(
receiveAll(receiveBuffer, timeoutSeconds, retransmitMaxRetries, verbose) < 0)
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__;
710 __COUTT__ <<
" ----> Time sendAndReceiveAll complete: " << receiveBuffer.size()
711 <<
" bytes (socketNumber=" << socketNumber_ <<
") ==> "
712 << std::chrono::duration_cast<std::chrono::milliseconds>(clock::now() -
715 <<
" milliseconds. PID=" << getpid()
716 <<
" TID=" << std::this_thread::get_id() << std::endl;
718 return receiveBuffer;
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