otsdaq  3.11.00
TCPServerBase.cc
1 // #ifndef BEAGLEBONE
2 // #include "otsdaq_cmsburninbox/BeagleBone/BeagleBoneUtils/TCPServerBase.h"
3 // #else
4 #include "otsdaq/NetworkUtilities/TCPServerBase.h"
5 #include "otsdaq/Macros/CoutMacros.h"
6 #include "otsdaq/NetworkUtilities/TCPTransmitterSocket.h"
7 
8 // #endif
9 
10 #include <arpa/inet.h>
11 #include <errno.h> // errno
12 #include <string.h> // errno
13 #include <sys/socket.h>
14 #include <iostream>
15 #include <thread>
16 
17 // #include <sys/socket.h>
18 // #include <netinet/in.h>
19 // #include <netdb.h>
20 
21 using namespace ots;
22 
23 //==============================================================================
24 TCPServerBase::TCPServerBase(unsigned int serverPort, unsigned int maxNumberOfClients)
25  : fMaxNumberOfClients(maxNumberOfClients), fServerPort(serverPort), fAccept(true)
26 {
27  // 0 or -1 means no restrictions on the number of clients
28  if(fMaxNumberOfClients == 0)
29  fMaxNumberOfClients = (unsigned)-1;
30  // CANNOT GO IN THE CONSTRUCTOR OR IT MIGHT START BEFORE THE CHILD CLASS CONSTRUCTOR IS FULLY CONSTRUCTED
31  // THIS MIGHT RESULT IN THE CALL OF THE VIRTUAL TCPServerBase::acceptConnections
32  // startAccept();
33 }
34 
35 //==============================================================================
36 TCPServerBase::~TCPServerBase(void)
37 {
38  __COUT__ << "Shutting down accept for socket: " << getSocketId() << std::endl;
39  shutdownAccept();
40  while(fAcceptFuture.valid() && fAcceptFuture.wait_for(std::chrono::milliseconds(
41  100)) != std::future_status::ready)
42  {
43  __COUT__ << "Server accept still running" << std::endl;
44  shutdownAccept();
45  }
46  //__COUT__ << "Closing connected client sockets for socket: " << getSocketId() << std::endl;
47  closeClientSockets();
48  //__COUT__ << "Closed all sockets connected to server: " << getSocketId() << std::endl;
49 }
50 
51 //==============================================================================
52 void TCPServerBase::startAccept(void)
53 {
54  struct sockaddr_in checkAddr;
55  socklen_t addrLen = sizeof(checkAddr);
56  if(::getsockname(getSocketId(), (struct sockaddr*)&checkAddr, &addrLen) == 0 &&
57  checkAddr.sin_port != 0)
58  {
59  __COUT__ << "Socket " << getSocketId() << " is already bound to port "
60  << ntohs(checkAddr.sin_port) << std::endl;
61  return;
62  }
63 
64  // __COUT__ << "Begin startAccept" << std::endl;
65  int opt = 1; // SO_REUSEADDR - man socket(7)
66  if(::setsockopt(getSocketId(), SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1)
67  {
68  close();
69  __SS__ << "Setsockopt: " << strerror(errno) << __E__;
70  __SS_THROW__;
71  }
72 
73  struct sockaddr_in serverAddr;
74  bzero((char*)&serverAddr, sizeof(serverAddr));
75  serverAddr.sin_family = AF_INET;
76  serverAddr.sin_port = htons(fServerPort);
77  serverAddr.sin_addr.s_addr = INADDR_ANY;
78 
79  if(::bind(getSocketId(), (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0)
80  {
81  close();
82  __SS__ << "Bind to port " << std::to_string(fServerPort) << ": "
83  << strerror(errno) << __E__;
84  __SS_THROW__;
85  }
86  // freeaddrinfo(serverAddr); // all done with this structure
87 
88  if(::listen(getSocketId(), fMaxConnectionBacklog) != 0)
89  {
90  close();
91  __SS__ << "Listen on port " << std::to_string(fServerPort) << ": "
92  << strerror(errno) << __E__;
93  __SS_THROW__;
94  }
95 
96  fAccept = true;
97  fAcceptFuture =
98  std::async(std::launch::async, &TCPServerBase::acceptConnections, this);
99  // __COUT__ << "Done startAccept" << std::endl;
100 }
101 
103 //==============================================================================
104 int TCPServerBase::accept(bool blocking)
105 {
106  __COUT__ << "Now server accept connections on socket: " << getSocketId() << std::endl;
107  if(getSocketId() == invalidSocketId)
108  {
109  __SS__ << "Accept called on a bad socket object (this object was moved)" << __E__;
110  __SS_THROW__;
111  }
112 
113  struct sockaddr_storage clientAddress; // connector's address information
114  socklen_t clientAddressSize = sizeof(clientAddress);
115  int clientSocket = invalidSocketId;
116  if(blocking)
117  {
118  //__COUT__ << "Number of connected clients: " << fConnectedClients.size() << std::endl;
119  // clientSocket = ::accept4(getSocketId(),(struct sockaddr *)&clientAddress, &clientAddressSize, 0);
120  // unsigned counter = 0;
121  {
122  std::lock_guard<std::mutex> lock(fClientsMutex);
123  __COUT__ << "Client list on input:\n";
124  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
125  {
126  __COUT__ << " --> Client: " << it->first << " : " << it->second
127  << std::endl;
128  }
129  }
130  while(true)
131  {
132  clientSocket = ::accept(
133  getSocketId(), (struct sockaddr*)&clientAddress, &clientAddressSize);
134  __COUT__ << ": clientSocket returned = " << clientSocket << std::endl;
135 
136  bool tooMany = false;
137  {
138  std::lock_guard<std::mutex> lock(fClientsMutex);
139  tooMany = fAccept && fMaxNumberOfClients > 0 &&
140  fConnectedClients.size() >= fMaxNumberOfClients;
141  }
142  if(tooMany)
143  {
144  send(clientSocket, "Too many clients connected!", 27, 0);
145  ::shutdown(clientSocket, SHUT_WR);
146  continue;
147  }
148  break;
149  }
150  __COUT__ << "fAccept? " << fAccept << std::endl;
151  if(!fAccept)
152  {
153  throw E_SHUTDOWN;
154  }
155  else if(clientSocket == invalidSocketId)
156  {
157  __COUT__ << "New socket invalid?: " << clientSocket << " errno: " << errno
158  << std::endl;
159  __SS__ << "Accept: " << strerror(errno) << __E__;
160  __SS_THROW__;
161  }
162 
163  __COUT__ << "Server just accepted a connection on socket: " << getSocketId()
164  << " Client socket: " << clientSocket << std::endl;
165  return clientSocket;
166  }
167  else
168  {
169  constexpr int sleepMSeconds = 5;
170  constexpr int timeoutSeconds = 0;
171  constexpr int timeoutUSeconds = 1000;
172  struct timeval timeout;
173  timeout.tv_sec = timeoutSeconds;
174  timeout.tv_usec = timeoutUSeconds;
175 
176  fd_set fdSet;
177 
178  while(fAccept)
179  {
180  FD_ZERO(&fdSet);
181  FD_SET(getSocketId(), &fdSet);
182  select(getSocketId() + 1, &fdSet, 0, 0, &timeout);
183 
184  if(FD_ISSET(getSocketId(), &fdSet))
185  {
186  struct sockaddr_in clientAddress;
187  socklen_t socketSize = sizeof(clientAddress);
188  // int newSocketFD = ::accept4(fdServerSocket_,(struct sockaddr*)&clientAddress,&socketSize, (pushOnly_ ? SOCK_NONBLOCK : 0));
189  clientSocket = ::accept(
190  getSocketId(),
191  (struct sockaddr*)&clientAddress,
192  &socketSize); // Blocking since select goes in timeout if there is nothing
193  if(clientSocket == invalidSocketId)
194  {
195  __COUT__ << "New socket invalid?: " << clientSocket
196  << " errno: " << errno << std::endl;
197  __SS__ << "Accept: " << strerror(errno) << __E__;
198  __SS_THROW__;
199  }
200  return clientSocket;
201  }
202  std::this_thread::sleep_for(std::chrono::milliseconds(sleepMSeconds));
203  }
204  throw E_SHUTDOWN;
205  }
206 }
207 
208 //==============================================================================
210 void TCPServerBase::closeClientSockets(void)
211 {
212  for(auto& socket : fConnectedClients)
213  {
214  try
215  {
216  socket.second->sendClose();
217  }
218  catch(const std::exception& e)
219  {
220  // I can get here with the TCPPubishServer because it doesn't keep track of the clients that might have already disconnected
221  // Just do nothing!
222  __COUT__ << e.what() << '\n';
223  }
224 
225  auto clientThread = fConnectedClientsFuture.find(socket.first);
226  if(clientThread != fConnectedClientsFuture.end())
227  clientThread->second.wait(); // Waiting for client thread
228  delete socket.second;
229  }
230  fConnectedClients.clear();
231  fConnectedClientsFuture.clear();
232 }
233 
234 //==============================================================================
235 void TCPServerBase::removeClient(int socketId)
236 {
237  std::lock_guard<std::mutex> lock(fClientsMutex);
238  auto it = fConnectedClients.find(socketId);
239  if(it == fConnectedClients.end())
240  return;
241  auto clientThread = fConnectedClientsFuture.find(socketId);
242  if(clientThread != fConnectedClientsFuture.end())
243  fConnectedClientsFuture.erase(clientThread);
244  delete it->second; // TCPSocket destructor closes the descriptor
245  fConnectedClients.erase(it);
246  __COUT__ << "Removed disconnected client socket " << socketId << ", "
247  << fConnectedClients.size() << " remaining." << std::endl;
248 }
249 
250 //==============================================================================
251 std::vector<int> TCPServerBase::getClientSocketIds(void) const
252 {
253  std::lock_guard<std::mutex> lock(fClientsMutex);
254  std::vector<int> ids;
255  ids.reserve(fConnectedClients.size());
256  for(auto const& client : fConnectedClients)
257  ids.push_back(client.first);
258  return ids;
259 }
260 
261 //==============================================================================
262 void TCPServerBase::closeClientSocket(int socket)
263 {
264  // This method is called inside the thread itself so it cannot call the removeClientSocketFuture!!!
265  std::lock_guard<std::mutex> lock(fClientsMutex);
266  auto it = fConnectedClients.find(socket);
267  if(it != fConnectedClients.end())
268  {
269  if(it->second->getSocketId() == socket)
270  {
271  try
272  {
273  if(it->second != nullptr)
274  it->second->sendClose();
275  }
276  catch(const std::exception& e)
277  {
278  // I can get here with the TCPPubishServer because it doesn't keep track of the clients that might have already disconnected
279  // Just do nothing!
280  __COUT__ << e.what() << '\n';
281  }
282  delete it->second;
283  fConnectedClients.erase(it);
284  }
285  else
286  {
287  __SS__
288  << "SocketId in fConnectedClients != socketId in TCPSocket! Impossible!!!"
289  << __E__;
290  __SS_THROW__;
291  }
292  }
293 }
294 
295 //==============================================================================
296 void TCPServerBase::broadcastPacket(const char* message, std::size_t length)
297 {
298  broadcastPacket(std::string(message, length));
299 }
300 
301 //==============================================================================
302 void TCPServerBase::broadcastPacket(const std::string& message)
303 {
304  std::lock_guard<std::mutex> lock(fClientsMutex);
305  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
306  {
307  try
308  {
309  if(auto sock = dynamic_cast<TCPTransmitterSocket*>(it->second);
310  sock != nullptr)
311  sock->sendPacket(message);
312  }
313  catch(const std::exception& e)
314  {
315  // __COUT__ << "I don't think that this error is possible because I close the socket when I get disconnected...if you see this then you should
316  // contact Lorenzo Uplegger" << std::endl;
317  // __COUT__ << "This should only happen with the TCPSubscribeServer because it doesn't keep track of the connected clients..." << std::endl;
318  // __COUT__ << "Error: " << e.what() << std::endl;
319  if(fConnectedClientsFuture.find(it->first) != fConnectedClientsFuture.end())
320  fConnectedClientsFuture.erase(fConnectedClientsFuture.find(it->first));
321  delete it->second;
322  fConnectedClients.erase(it--);
323  }
324  }
325 }
326 
327 //========================================================================================================================
328 void TCPServerBase::broadcast(const char* message, std::size_t length)
329 {
330  std::lock_guard<std::mutex> lock(fClientsMutex);
331  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
332  {
333  try
334  {
335  if(auto sock = dynamic_cast<TCPTransmitterSocket*>(it->second);
336  sock != nullptr)
337  sock->send(message, length);
338  }
339  catch(const std::exception& e)
340  {
341  // __COUT__ << "I don't think that this error is possible because I close the socket when I get disconnected...if you see this then you should
342  // contact Lorenzo Uplegger" << std::endl;
343  // __COUT__ << "This should only happen with the TCPSubscribeServer because it doesn't keep track of the connected clients..." << std::endl;
344  // __COUT__ << "Error: " << e.what() << std::endl;
345  if(fConnectedClientsFuture.find(it->first) != fConnectedClientsFuture.end())
346  fConnectedClientsFuture.erase(fConnectedClientsFuture.find(it->first));
347  delete it->second;
348  fConnectedClients.erase(it--);
349  }
350  }
351 }
352 
353 //==============================================================================
354 void TCPServerBase::broadcast(const std::string& message)
355 {
356  std::lock_guard<std::mutex> lock(fClientsMutex);
357  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
358  {
359  try
360  {
361  if(auto sock = dynamic_cast<TCPTransmitterSocket*>(it->second);
362  sock != nullptr)
363  sock->send(message);
364  }
365  catch(const std::exception& e)
366  {
367  // __COUT__ << "I don't think that this error is possible because I close the socket when I get disconnected...if you see this then you should
368  // contact Lorenzo Uplegger" << std::endl;
369  // __COUT__ << "This should only happen with the TCPSubscribeServer because it doesn't keep track of the connected clients..." << std::endl;
370  // __COUT__ << "Error: " << e.what() << std::endl;
371  if(fConnectedClientsFuture.find(it->first) != fConnectedClientsFuture.end())
372  fConnectedClientsFuture.erase(fConnectedClientsFuture.find(it->first));
373  delete it->second;
374  fConnectedClients.erase(it--);
375  }
376  }
377 }
378 
379 //==============================================================================
380 void TCPServerBase::broadcast(const std::vector<char>& message)
381 {
382  std::lock_guard<std::mutex> lock(fClientsMutex);
383  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
384  {
385  try
386  {
387  if(auto sock = dynamic_cast<TCPTransmitterSocket*>(it->second);
388  sock != nullptr)
389  sock->send(message);
390  }
391  catch(const std::exception& e)
392  {
393  // __COUT__ << "I don't think that this error is possible because I close the socket when I get disconnected...if you see this then you should
394  // contact Lorenzo Uplegger" << std::endl;
395  // __COUT__ << "This should only happen with the TCPSubscribeServer because it doesn't keep track of the connected clients..." << std::endl;
396  // __COUT__ << "Error: " << e.what() << std::endl;
397  if(fConnectedClientsFuture.find(it->first) != fConnectedClientsFuture.end())
398  fConnectedClientsFuture.erase(fConnectedClientsFuture.find(it->first));
399  delete it->second;
400  fConnectedClients.erase(it--);
401  }
402  }
403 }
404 
405 //==============================================================================
406 void TCPServerBase::broadcast(const std::vector<uint16_t>& message)
407 {
408  std::lock_guard<std::mutex> lock(fClientsMutex);
409  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
410  {
411  try
412  {
413  if(auto sock = dynamic_cast<TCPTransmitterSocket*>(it->second);
414  sock != nullptr)
415  sock->send(message);
416  }
417  catch(const std::exception& e)
418  {
419  // __COUT__ << "This should only happen with the TCPSubscribeServer because it doesn't keep track of the connected clients..." << std::endl;
420  __COUT__ << "Error: " << e.what() << std::endl;
421  if(fConnectedClientsFuture.find(it->first) != fConnectedClientsFuture.end())
422  {
423  __COUT__ << "Removing client entry from future connected clients list\n";
424  fConnectedClientsFuture.erase(fConnectedClientsFuture.find(it->first));
425  }
426  __COUT__ << "Removing client entry from connected clients list\n";
427  delete it->second;
428  fConnectedClients.erase(it--);
429  }
430  }
431 }
432 
433 //==============================================================================
434 void TCPServerBase::pingActiveClients()
435 {
436  std::lock_guard<std::mutex> lock(fClientsMutex);
437  for(auto it = fConnectedClients.begin(); it != fConnectedClients.end(); it++)
438  {
439  __COUT__ << "Pinging client " << it->first << " : " << it->second << std::endl;
440  try
441  {
442  if(auto sock = dynamic_cast<TCPTransmitterSocket*>(it->second);
443  sock != nullptr)
444  sock->send("", 0, true);
445  }
446  catch(const std::exception& e)
447  {
448  // __COUT__ << "I don't think that this error is possible because I close the socket when I get disconnected...if you see this then you should
449  // contact Lorenzo Uplegger" << std::endl;
450  // __COUT__ << "This should only happen with the TCPSubscribeServer because it doesn't keep track of the connected clients..." << std::endl;
451  __COUT__ << "Error: " << e.what() << std::endl;
452  if(fConnectedClientsFuture.find(it->first) != fConnectedClientsFuture.end())
453  {
454  __COUT__ << "Removing client entry from future connected clients list\n";
455  fConnectedClientsFuture.erase(fConnectedClientsFuture.find(it->first));
456  }
457  __COUT__ << "Removing client entry from connected clients list\n";
458  delete it->second;
459  fConnectedClients.erase(it--);
460  }
461  }
462 }
463 
464 //==============================================================================
465 void TCPServerBase::shutdownAccept()
466 {
467  fAccept = false;
468  shutdown(getSocketId(), SHUT_RD);
469 }
TCPServerBase(unsigned int serverPort, unsigned int maxNumberOfClients=0)
Means as many unsigned allows.
void removeClient(int socketId)
Drop a client whose peer has gone away (no shutdown handshake, just close).
std::vector< int > getClientSocketIds(void) const
Snapshot of connected client socket ids, safe to call from any thread.
A class that can write to a socket.
defines used also by OtsConfigurationWizardSupervisor