source: flair-src/trunk/lib/FlairCore/src/TcpSocket.cpp @ 124

Last change on this file since 124 was 15, checked in by Bayard Gildas, 6 years ago

sources reformatted with flair-format-dir script

File size: 7.1 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
25using std::string;
26
27namespace flair {
28namespace core {
29
30TcpSocket::TcpSocket(const Object *parent, const std::string name,
31                     bool _blockOnSend, bool _blockOnReceive)
32    : ConnectedSocket(parent, name), isConnected(false) {
33  blockOnSend = _blockOnSend;
34  blockOnReceive = _blockOnReceive;
35}
36
37TcpSocket::~TcpSocket() {
38  // Info("Debug: destroying TCP socket %s", ObjectName().c_str());
39  close(socket);
40}
41
42void TcpSocket::Listen(const unsigned int port,
43                       const std::string localAddress) {
44  socket = ::socket(AF_INET, SOCK_STREAM, 0);
45
46  sockaddr_in my_addr;
47  my_addr.sin_family = AF_INET;
48  my_addr.sin_port = htons(port);
49  if (localAddress == "ANY") {
50    my_addr.sin_addr.s_addr = INADDR_ANY;
51  } else {
52    inet_aton(localAddress.c_str(), &(my_addr.sin_addr));
53  }
54  memset(&(my_addr.sin_zero), '\0', 8);
55
56  if (bind(socket, (sockaddr *)&my_addr, sizeof(my_addr)) < 0) {
57    char errorMsg[256];
58    Err("TCP bind, %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
59  }
60
61  listen(socket, 1);
62}
63
64TcpSocket *TcpSocket::Accept(Time timeout) {
65  char errorMsg[256];
66  TcpSocket *acceptedSocket = nullptr;
67
68  struct timeval tv;
69  if (timeout != 0) {
70    tv.tv_sec = timeout / 1000; // timeout is in ms
71    tv.tv_usec = (timeout % 1000) * 1000;
72  }
73  fd_set rset;
74  FD_ZERO(&rset);
75  FD_SET(socket, &rset);
76  int ret = select(socket + 1, &rset, nullptr, nullptr,
77                   (timeout == 0) ? nullptr : &tv); // man 2 accept
78  if (ret < 0) {
79    Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
80  } else {
81    if (ret == 0) {
82      // timeout reached
83      // Err("timeout reached\n");
84    } else {
85      // our socket is readable, a new connection can be accepted
86      acceptedSocket = new TcpSocket(this->Parent(), this->ObjectName(),
87                                     blockOnSend, blockOnReceive);
88      sockaddr_in their_addr;
89      socklen_t namelen = sizeof(their_addr);
90      if ((acceptedSocket->socket =
91               accept(socket, (sockaddr *)&their_addr, &namelen)) < 0) {
92        Err("error: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
93        delete acceptedSocket;
94        acceptedSocket = nullptr;
95      }
96    }
97  }
98
99  return acceptedSocket;
100}
101
102bool TcpSocket::Connect(const unsigned int distantPort,
103                        const std::string distantAddress, Time timeout) {
104  bool success = false;
105  char errorMsg[256];
106
107  if (isConnected) {
108    if (this->distantPort == distantPort &&
109        this->distantAddress == distantAddress) {
110      return true;
111    } else {
112      close(socket);
113    }
114  }
115  socket = ::socket(AF_INET, SOCK_STREAM, 0);
116  if (socket == -1)
117    return false;
118
119  sockaddr_in serv_addr;
120  serv_addr.sin_family = AF_INET;
121  serv_addr.sin_port = htons(short(distantPort));
122  if (inet_pton(AF_INET, distantAddress.c_str(), &serv_addr.sin_addr) <= 0) {
123    printf("incorrect network address.");
124    close(socket);
125    return false;
126  }
127  memset(&(serv_addr.sin_zero), '\0', 8);
128
129  // go non blocking
130  int flags = fcntl(socket, F_GETFL);
131  fcntl(socket, F_SETFL, flags | O_NONBLOCK);
132  if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
133    if ((errno != EINPROGRESS) && (errno != EAGAIN)) {
134      Err("socket connect: %s\n",
135          strerror_r(errno, errorMsg, sizeof(errorMsg)));
136      success = false;
137    } else {
138      // now block with a timeout
139      struct timeval tv;
140      if (timeout != 0) {
141        tv.tv_sec = timeout / 1000; // timeout is in ms
142        tv.tv_usec = (timeout % 1000) * 1000;
143      }
144      fd_set wset;
145      FD_ZERO(&wset);
146      FD_SET(socket, &wset);
147      int ret =
148          select(socket + 1, NULL, &wset, NULL,
149                 (timeout == 0) ? NULL : &tv); // man 2 connect EINPROGRESS
150      if (ret < 0) {
151        Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
152        success = false;
153      } else {
154        if (ret == 0) {
155          // timeout reached
156          // Err("timeout reached\n");
157          success = false;
158        } else {
159          // something happened on our socket. Check if an error occured
160          int error;
161          socklen_t len = sizeof(error);
162          if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
163            // Err("getsockopt:
164            // %s\n",strerror_r(errno,errorMsg,sizeof(errorMsg)));
165            success = false;
166          } else if (error != 0) {
167            // Err("socket error:
168            // %d(%s)\n",error,strerror_r(error,errorMsg,sizeof(errorMsg)));
169            success = false;
170          } else {
171            if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) ==
172                -1) {
173              success = false;
174            } else {
175              // Info("connected indeed ^^\n");
176              success = true;
177            }
178          }
179        }
180      }
181    }
182  } else {
183    success = true; // never reached suprisingly...
184  }
185  // switch back to blocking mode (default)
186  fcntl(socket, F_SETFL, flags);
187
188  if (!success) {
189    close(socket);
190    // Info("Debug: Connect to %s:%d failed\n", distantAddress.c_str(),
191    // distantPort);
192    return false;
193  } else {
194    isConnected = true;
195    this->distantPort = distantPort;
196    this->distantAddress = distantAddress;
197    // Info("Debug: Connect to %s:%d succeeded\n", distantAddress.c_str(),
198    // distantPort);
199    return true;
200  }
201}
202
203ssize_t TcpSocket::SendMessage(const char *buffer, size_t bufferSize,
204                               Time timeout) {
205  int flags = 0;
206  if (!blockOnSend) {
207    flags |= MSG_DONTWAIT;
208  } else {
209    struct timeval tv;
210    tv.tv_sec = timeout / 1000; // timeout is in ms
211    tv.tv_usec = (timeout % 1000) * 1000;
212
213    setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,
214               sizeof(struct timeval));
215  }
216  ssize_t bytesSent = send(socket, buffer, bufferSize, flags);
217
218  return bytesSent;
219}
220
221ssize_t TcpSocket::RecvMessage(char *buffer, size_t bufferSize, Time timeout) {
222  int flags = 0;
223  if (!blockOnReceive) {
224    flags |= MSG_DONTWAIT;
225  } else {
226    struct timeval tv;
227    tv.tv_sec = timeout / 1000; // timeout is in ms
228    tv.tv_usec = (timeout % 1000) * 1000;
229
230    setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
231               sizeof(struct timeval));
232  }
233  ssize_t bytesRead = recv(socket, buffer, bufferSize, flags);
234
235  return bytesRead;
236}
237
238uint16_t TcpSocket::NetworkToHost16(uint16_t data) { return ntohs(data); }
239
240uint16_t TcpSocket::HostToNetwork16(uint16_t data) { return htons(data); }
241
242uint32_t TcpSocket::NetworkToHost32(uint32_t data) { return ntohl(data); }
243
244uint32_t TcpSocket::HostToNetwork32(uint32_t data) { return htonl(data); }
245
246} // end namespace core
247} // end namespace flair
Note: See TracBrowser for help on using the repository browser.