otsdaq  v2_04_01
TCPSocket.cc
1 #include "otsdaq-core/NetworkUtilities/TCPSocket.h"
2 #include "artdaq-core/Utilities/TimeUtils.hh"
3 #include "otsdaq-core/Macros/CoutMacros.h"
4 #include "otsdaq-core/MessageFacility/MessageFacility.h"
5 
6 #include <cassert>
7 #include <iostream>
8 #include <sstream>
9 
10 #include <arpa/inet.h>
11 #include <unistd.h>
12 //#include <sys/TCPSocket.h>
13 #include <netdb.h>
14 //#include <ifaddrs.h>
15 //#include <sys/ioctl.h>
16 //#if defined(SIOCGIFHWADDR)
17 //#include <net/if.h>
18 //#else
19 //#include <net/if_dl.h>
20 //#endif
21 //#include <cstdlib>
22 #include <cstring>
23 //#include <cstdio>
24 #include "trace.h"
25 
26 using namespace ots;
27 
28 //========================================================================================================================
29 TCPSocket::TCPSocket(const std::string& senderHost,
30  unsigned int senderPort,
31  int receiveBufferSize)
32  : host_(senderHost)
33  , port_(senderPort)
34  , TCPSocketNumber_(-1)
35  , SendSocket_(-1)
36  , isSender_(false)
37  , bufferSize_(receiveBufferSize)
38  , chunkSize_(65000)
39 {
40 }
41 
42 //========================================================================================================================
43 TCPSocket::TCPSocket(unsigned int listenPort, int sendBufferSize)
44  : port_(listenPort)
45  , TCPSocketNumber_(-1)
46  , SendSocket_(-1)
47  , isSender_(true)
48  , bufferSize_(sendBufferSize)
49  , chunkSize_(65000)
50 {
51  TCPSocketNumber_ = TCP_listen_fd(listenPort, bufferSize_);
52 }
53 
54 //========================================================================================================================
55 // protected constructor
56 TCPSocket::TCPSocket(void)
57 {
58  __SS__ << "ERROR: This method should never be called. This is the protected "
59  "constructor. There is something wrong in your inheritance scheme!"
60  << std::endl;
61  __COUT__ << "\n" << ss.str();
62 
63  __SS_THROW__;
64 }
65 
66 //========================================================================================================================
67 TCPSocket::~TCPSocket(void)
68 {
69  __COUT__ << "CLOSING THE TCPSocket #" << TCPSocketNumber_ << " IP: " << host_
70  << " port: " << port_ << std::endl;
71  if(TCPSocketNumber_ != -1)
72  close(TCPSocketNumber_);
73  if(SendSocket_ != -1)
74  close(SendSocket_);
75 }
76 
77 //========================================================================================================================
78 void TCPSocket::connect(double tmo_s)
79 {
80  auto start = std::chrono::steady_clock::now();
81  if(isSender_)
82  {
83  while(SendSocket_ == -1 && artdaq::TimeUtils::GetElapsedTime(start) < tmo_s)
84  {
85  sockaddr_in addr;
86  socklen_t arglen = sizeof(addr);
87  SendSocket_ = accept(TCPSocketNumber_, (struct sockaddr*)&addr, &arglen);
88 
89  if(SendSocket_ == -1)
90  {
91  perror("accept");
92  continue;
93  }
94 
95  MagicPacket m;
96  auto sts = read(SendSocket_, &m, sizeof(MagicPacket));
97  if(!checkMagicPacket(m) || sts != sizeof(MagicPacket))
98  {
99  perror("magic");
100  close(SendSocket_);
101  SendSocket_ = -1;
102  }
103  }
104  }
105  else
106  {
107  while(TCPSocketNumber_ == -1 && artdaq::TimeUtils::GetElapsedTime(start) < tmo_s)
108  {
109  TCPSocketNumber_ = TCPConnect(host_.c_str(), port_, 0, O_NONBLOCK);
110  if(TCPSocketNumber_ == -1)
111  {
112  usleep(10000);
113  continue;
114  }
115 
116  auto m = makeMagicPacket(port_);
117  auto sts = ::send(TCPSocketNumber_, &m, sizeof(MagicPacket), 0);
118  if(sts != sizeof(MagicPacket))
119  {
120  perror("connect");
121  close(TCPSocketNumber_);
122  TCPSocketNumber_ = -1;
123  }
124  }
125  }
126 }
127 
128 //========================================================================================================================
129 int TCPSocket::send(const uint8_t* data, size_t size)
130 {
131  std::unique_lock<std::mutex> lk(socketMutex_);
132  if(SendSocket_ == -1)
133  {
134  connect();
135 
136  if(SendSocket_ == -1)
137  return -1;
138  }
139 
140  size_t offset = 0;
141  int sts = 1;
142 
143  while(offset < size && sts > 0)
144  {
145  auto thisSize = size - offset > chunkSize_ ? chunkSize_ : size - offset;
146 
147  sts = ::send(SendSocket_, data + offset, thisSize, 0);
148 
149  // Dynamically resize chunk size to match send calls
150  if(static_cast<size_t>(sts) != size - offset &&
151  static_cast<size_t>(sts) < chunkSize_)
152  {
153  chunkSize_ = sts;
154  }
155  offset += sts;
156  }
157 
158  if(sts <= 0)
159  {
160  __COUT__ << "Error writing buffer for port " << port_ << ": " << strerror(errno)
161  << std::endl;
162  return -1;
163  }
164  return 0;
165 }
166 
167 //========================================================================================================================
168 int TCPSocket::send(const std::string& buffer)
169 {
170  return send(reinterpret_cast<const uint8_t*>(buffer.c_str()), buffer.size());
171 }
172 
173 //========================================================================================================================
174 int TCPSocket::send(const std::vector<uint16_t>& buffer)
175 {
176  return send(reinterpret_cast<const uint8_t*>(&buffer[0]),
177  buffer.size() * sizeof(uint16_t));
178 }
179 
180 //========================================================================================================================
181 int TCPSocket::send(const std::vector<uint32_t>& buffer)
182 {
183  return send(reinterpret_cast<const uint8_t*>(&buffer[0]),
184  buffer.size() * sizeof(uint32_t));
185 }
186 
187 //========================================================================================================================
188 // receive ~~
189 // returns 0 on success, -1 on failure
190 // NOTE: must call Socket::initialize before receiving!
191 int TCPSocket::receive(uint8_t* buffer,
192  unsigned int timeoutSeconds,
193  unsigned int timeoutUSeconds)
194 {
195  // lockout other receivers for the remainder of the scope
196  std::unique_lock<std::mutex> lk(socketMutex_);
197 
198  if(TCPSocketNumber_ == -1)
199  {
200  connect();
201  if(TCPSocketNumber_ == -1)
202  return -1;
203  }
204 
205  // set timeout period for select()
206  struct timeval timeout;
207  timeout.tv_sec = timeoutSeconds;
208  timeout.tv_usec = timeoutUSeconds;
209 
210  fd_set fdSet;
211  FD_ZERO(&fdSet);
212  FD_SET(TCPSocketNumber_, &fdSet);
213  select(TCPSocketNumber_ + 1, &fdSet, 0, 0, &timeout);
214 
215  if(FD_ISSET(TCPSocketNumber_, &fdSet))
216  {
217  auto sts = -1;
218  if((sts = read(TCPSocketNumber_, buffer, chunkSize_)) == -1)
219  {
220  __COUT__ << "Error reading buffer from: " << host_ << ":" << port_
221  << std::endl;
222  return -1;
223  }
224 
225  // NOTE: this is inexpensive according to Lorenzo/documentation in C++11 (only
226  // increases size once and doesn't decrease size)
227  return sts;
228  }
229 
230  return -1;
231 }
232 
233 //========================================================================================================================
234 // receive ~~
235 // returns 0 on success, -1 on failure
236 // NOTE: must call Socket::initialize before receiving!
237 int TCPSocket::receive(std::string& buffer,
238  unsigned int timeoutSeconds,
239  unsigned int timeoutUSeconds)
240 {
241  buffer.resize(chunkSize_);
242  auto sts =
243  receive(reinterpret_cast<uint8_t*>(&buffer[0]), timeoutSeconds, timeoutUSeconds);
244  if(sts > 0)
245  {
246  buffer.resize(sts);
247  return 0;
248  }
249  return -1;
250 }
251 
252 //========================================================================================================================
253 // receive ~~
254 // returns 0 on success, -1 on failure
255 // NOTE: must call Socket::initialize before receiving!
256 int TCPSocket::receive(std::vector<uint32_t>& buffer,
257  unsigned int timeoutSeconds,
258  unsigned int timeoutUSeconds)
259 {
260  buffer.resize(chunkSize_ / sizeof(uint32_t));
261  auto sts =
262  receive(reinterpret_cast<uint8_t*>(&buffer[0]), timeoutSeconds, timeoutUSeconds);
263  if(sts > 0)
264  {
265  buffer.resize(sts);
266  return 0;
267  }
268  return -1;
269 }