source: flair-src/branches/sanscv/lib/FlairCore/src/TcpSocket.cpp @ 326

Last change on this file since 326 was 324, checked in by Sanahuja Guillaume, 2 years ago

removing opencv dependency

File size: 8.2 KB
Line 
1// %flair:license{
2// This file is part of the Flair framework distributed under the
3// CECILL-C License, Version 1.0.
4// %flair:license}
5//  created:    2015/04/28
6//  filename:   TcpSocket.cpp
7//
8//  author:     Gildas Bayard
9//              Copyright Heudiasyc UMR UTC/CNRS 7253
10//
11//  version:    $Id: $
12//
13//  purpose:    Class defining a TCP socket
14//
15//
16/*********************************************************************/
17#include "TcpSocket.h"
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21#include <unistd.h>
22#include <fcntl.h>
23#include <string.h>
24#include <system_error>
25
26using std::string;
27
28namespace flair {
29namespace core {
30
31TcpSocket::TcpSocket(const Object *parent, const std::string name,
32                     bool _blockOnSend, bool _blockOnReceive)
33    : ConnectedSocket(parent, name), socket(0), isConnected(false), isListening(false), distantPort(0), distantAddress() {
34  blockOnSend = _blockOnSend;
35  blockOnReceive = _blockOnReceive;
36}
37
38TcpSocket::~TcpSocket() {
39  // Info("Debug: destroying TCP socket %s", ObjectName().c_str());
40  if (socket) close(socket);
41}
42
43void TcpSocket::Listen(const unsigned int port,
44                       const std::string localAddress) {
45  socket = ::socket(AF_INET, SOCK_STREAM, 0);
46
47  sockaddr_in my_addr;
48  my_addr.sin_family = AF_INET;
49  my_addr.sin_port = htons(port);
50  if (localAddress == "ANY") {
51    my_addr.sin_addr.s_addr = INADDR_ANY;
52  } else {
53    inet_aton(localAddress.c_str(), &(my_addr.sin_addr));
54  }
55  memset(&(my_addr.sin_zero), '\0', 8);
56
57  if (bind(socket, (sockaddr *)&my_addr, sizeof(my_addr)) < 0) {
58    char errorMsg[256];
59    Err("TCP bind, %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
60  }
61
62  int ret=listen(socket, 1);
63  if (ret < 0) {
64    char errorMsg[256];
65    Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
66  } else {
67    isListening=true;
68  }
69}
70
71TcpSocket *TcpSocket::Accept(Time timeout) {
72  if (!isListening) throw std::logic_error("Can't call Accept on a non listening socket");
73
74  TcpSocket *acceptedSocket = nullptr;
75
76  struct timeval tv;
77  if (timeout != 0) {
78    tv.tv_sec = timeout / 1000000000;
79    tv.tv_usec = (timeout % 1000000000) / 1000;
80  }
81  fd_set rset;
82  FD_ZERO(&rset);
83  FD_SET(socket, &rset);
84  int ret = select(socket + 1, &rset, nullptr, nullptr,
85                   (timeout == 0) ? nullptr : &tv); // man 2 accept
86  if (ret < 0) {
87    char errorMsg[256];
88    Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
89  } else {
90    if (ret == 0) {
91      // timeout reached
92      throw std::runtime_error("Timeout");
93    } else {
94      // our socket is readable, a new connection can be accepted
95      acceptedSocket = new TcpSocket(this->Parent(), this->ObjectName(),
96                                     blockOnSend, blockOnReceive);
97      sockaddr_in their_addr;
98      socklen_t namelen = sizeof(their_addr);
99      ret = accept(socket, (sockaddr *)&their_addr, &namelen);
100      if (ret < 0) {
101        delete acceptedSocket;
102        throw std::system_error(std::error_code(ret, std::generic_category()));
103      } else {
104        acceptedSocket->socket = ret;
105      }
106    }
107  }
108
109  return acceptedSocket;
110}
111
112bool TcpSocket::Connect(const unsigned int _distantPort,
113                        const std::string _distantAddress, Time timeout) {
114  bool success = false;
115  bool criticalError=false; //meaning: if true then this socket can't be re-used
116
117  //should we open a new socket, close it, or re-use it?
118  if (isConnected) {
119    // if already connected to the same host, just return true
120    if (distantPort == _distantPort && distantAddress == _distantAddress) {
121      return true;
122    }
123    // if already connected, but to a different host, close the current socket
124    else {
125      isConnected=false;
126      close(socket);
127      socket=0;
128    }
129  }
130  if (!socket) {
131    socket = ::socket(AF_INET, SOCK_STREAM, 0);
132    if (socket == -1)
133      return false;
134  }
135
136  sockaddr_in serv_addr;
137  serv_addr.sin_family = AF_INET;
138  serv_addr.sin_port = htons(short(_distantPort));
139  if (inet_pton(AF_INET, _distantAddress.c_str(), &serv_addr.sin_addr) <= 0) {
140    Warn("incorrect network address.\n");
141    close(socket);
142    socket=0;
143    return false;
144  }
145  memset(&(serv_addr.sin_zero), '\0', 8);
146  distantPort=_distantPort;
147  distantAddress=_distantAddress;
148
149  // go non blocking
150  int flags = fcntl(socket, F_GETFL);
151  fcntl(socket, F_SETFL, flags | O_NONBLOCK);
152  if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
153    if ((errno != EINPROGRESS) && (errno != EAGAIN)) {
154      char errorMsg[256];
155      Err("socket connect: %s\n",
156          strerror_r(errno, errorMsg, sizeof(errorMsg)));
157      criticalError = true;
158    } else {
159      // now block with a timeout
160      struct timeval tv;
161      if (timeout != 0) {// timeout is in ns
162        tv.tv_sec = timeout /((Time)1000000000); 
163        tv.tv_usec = (timeout%((Time)1000000000))/1000;
164      }
165      fd_set wset;
166      FD_ZERO(&wset);
167      FD_SET(socket, &wset);
168      int ret =
169          select(socket + 1, NULL, &wset, NULL,
170                 (timeout == 0) ? NULL : &tv); // man 2 connect EINPROGRESS
171      if (ret < 0) {
172        char errorMsg[256];
173        Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
174        criticalError = true;
175      } else {
176        if (ret != 0) { //if ret==0 we're on timeout (nothing to do since success is already set to false)
177          // something happened on our socket. Check if an error occurred
178          int error;
179          socklen_t len = sizeof(error);
180          if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
181            //char errorMsg[256];
182            // Err("getsockopt: %s\n",strerror_r(errno,errorMsg,sizeof(errorMsg)));
183            criticalError = true;
184          } else if (error != 0) {
185            //char errorMsg[256];
186            // Err("socket error: %d(%s)\n",error,strerror_r(error,errorMsg,sizeof(errorMsg)));
187            criticalError = true;
188          } else {
189            if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) != -1) {
190              // Info("connected indeed ^^\n");
191              success = true;
192            } else {
193              //connect failed
194              if ((errno != EINPROGRESS) && (errno != EAGAIN)) {
195                criticalError = true;
196              }
197            }
198          }
199        }
200      }
201    }
202  } else {
203    success = true; // should never happen since we go non blocking and connect can't be immediate
204  }
205  // switch back to blocking mode (default)
206  fcntl(socket, F_SETFL, flags);
207
208  if (!success) {
209    if (criticalError) {
210      close(socket);
211      socket=0;
212    }
213    isConnected=false;
214     //Info("Debug: Connect to %s:%d failed\n", distantAddress.c_str(), distantPort);
215    return false;
216  } else {
217    isConnected = true;
218    distantPort = _distantPort;
219    distantAddress = _distantAddress;
220     //Info("Debug: Connect to %s:%d succeeded\n", distantAddress.c_str(), distantPort);
221    return true;
222  }
223}
224
225ssize_t TcpSocket::SendMessage(const char *buffer, size_t bufferSize,
226                               Time timeout) {
227  int flags = 0;
228  if (!blockOnSend) {
229    flags |= MSG_DONTWAIT;
230  } else {
231    struct timeval tv;
232    tv.tv_sec = timeout / 1000000000; // timeout is in ns
233    tv.tv_usec = (timeout % 1000000000) / 1000;
234
235    setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,
236               sizeof(struct timeval));
237  }
238  ssize_t bytesSent = send(socket, buffer, bufferSize, flags);
239
240  return bytesSent;
241}
242
243ssize_t TcpSocket::RecvMessage(char *buffer, size_t bufferSize, Time timeout) {
244  int flags = 0;
245  if (!blockOnReceive) {
246    flags |= MSG_DONTWAIT;
247  } else {
248    struct timeval tv;
249    tv.tv_sec = timeout / 1000000000; // timeout is in ns
250    tv.tv_usec = (timeout % 1000000000) / 1000;
251
252    setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
253               sizeof(struct timeval));
254  }
255  ssize_t bytesRead = recv(socket, buffer, bufferSize, flags);
256
257  return bytesRead;
258}
259
260uint16_t TcpSocket::NetworkToHost16(uint16_t data) { return ntohs(data); }
261
262uint16_t TcpSocket::HostToNetwork16(uint16_t data) { return htons(data); }
263
264uint32_t TcpSocket::NetworkToHost32(uint32_t data) { return ntohl(data); }
265
266uint32_t TcpSocket::HostToNetwork32(uint32_t data) { return htonl(data); }
267
268} // end namespace core
269} // end namespace flair
Note: See TracBrowser for help on using the repository browser.