otsdaq  3.11.00
TCPListenServer.cc
1 #include "otsdaq/NetworkUtilities/TCPListenServer.h"
2 #include "otsdaq/NetworkUtilities/TCPReceiverSocket.h"
3 
4 #include <poll.h>
5 #include <iostream>
6 #include <thread>
7 #include <utility>
8 #include <vector>
9 
10 using namespace ots;
11 
12 constexpr std::chrono::milliseconds TCPListenServer::kPollTimeout;
13 
14 //==============================================================================
15 TCPListenServer::TCPListenServer(unsigned int serverPort, unsigned int maxNumberOfClients)
16  : TCPServerBase(serverPort, maxNumberOfClients)
17 {
18 }
19 
20 //==============================================================================
21 TCPListenServer::~TCPListenServer(void)
22 {
23  // std::cout << __PRETTY_FUNCTION__ << "Done" << std::endl;
24 }
25 
26 //==============================================================================
28  std::chrono::milliseconds timeout)
29 {
30  // Snapshot under the lock; the accept thread may insert while we poll.
31  std::vector<std::pair<int, TCPSocket*>> clients;
32  {
33  std::lock_guard<std::mutex> lock(fClientsMutex);
34  clients.assign(fConnectedClients.begin(), fConnectedClients.end());
35  }
36 
37  if(clients.empty())
38  {
39  std::this_thread::sleep_for(timeout);
40  return nullptr;
41  }
42 
43  std::vector<struct pollfd> fds(clients.size());
44  for(size_t i = 0; i < clients.size(); ++i)
45  {
46  fds[i].fd = clients[i].first;
47  fds[i].events = POLLIN;
48  fds[i].revents = 0;
49  }
50 
51  int ready = ::poll(fds.data(), fds.size(), static_cast<int>(timeout.count()));
52  if(ready <= 0)
53  return nullptr; // timeout, or EINTR: caller simply tries again
54 
55  // Round-robin among the ready clients, starting after the one served last.
56  size_t start = 0;
57  for(size_t i = 0; i < clients.size(); ++i)
58  if(clients[i].first == lastReceived)
59  {
60  start = i + 1;
61  break;
62  }
63  for(size_t k = 0; k < clients.size(); ++k)
64  {
65  size_t i = (start + k) % clients.size();
66  // POLLHUP/POLLERR/POLLNVAL are "ready" too: the read will return 0 or fail,
67  // the caller throws, and the client is removed.
68  if(fds[i].revents & (POLLIN | POLLHUP | POLLERR | POLLNVAL))
69  {
70  lastReceived = clients[i].first;
71  std::lock_guard<std::mutex> lock(fClientsMutex);
72  auto it = fConnectedClients.find(clients[i].first);
73  if(it == fConnectedClients.end())
74  return nullptr;
75  TLOG(25, "TCPListenServer")
76  << "Reading from socket " << lastReceived << ", there are "
77  << fConnectedClients.size() << " clients connected.";
78  return dynamic_cast<TCPReceiverSocket*>(it->second);
79  }
80  }
81  return nullptr;
82 }
83 
84 //==============================================================================
86 {
87  TCPReceiverSocket* client = waitForReadableClient(kPollTimeout);
88  if(client == nullptr)
89  return "";
90  const int socketId = client->getSocketId();
91  try
92  {
93  // poll() said readable, so the length header is there or the peer hung up;
94  // once the header is in, the body follows from the same sender.
95  return client->receivePacket(std::chrono::milliseconds(100));
96  }
97  catch(...)
98  {
99  removeClient(socketId);
100  throw;
101  }
102 }
103 
104 //==============================================================================
105 void TCPListenServer::acceptConnections()
106 {
107  while(getAccept())
108  {
109  try
110  {
111  // __attribute__((unused)) TCPTransmitterSocket* clientSocket = acceptClient<TCPTransmitterSocket>();
112  acceptClient<TCPReceiverSocket>();
113  }
114  catch(int e)
115  {
116  if(e == E_SHUTDOWN)
117  break;
118  }
119  }
120  // fAcceptPromise.set_value(true);
121 }
TCPReceiverSocket * waitForReadableClient(std::chrono::milliseconds timeout)
std::string receivePacket()
Length-prefixed packet from a ready client. Same timeout/throw semantics.
void removeClient(int socketId)
Drop a client whose peer has gone away (no shutdown handshake, just close).
defines used also by OtsConfigurationWizardSupervisor