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