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

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

bug fix

File size: 8.2 KB
RevLine 
[2]1// %flair:license{
[15]2// This file is part of the Flair framework distributed under the
3// CECILL-C License, Version 1.0.
[2]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>
[238]24#include <system_error>
[2]25
26using std::string;
27
28namespace flair {
29namespace core {
30
[15]31TcpSocket::TcpSocket(const Object *parent, const std::string name,
32 bool _blockOnSend, bool _blockOnReceive)
[238]33 : ConnectedSocket(parent, name), socket(0), isConnected(false), isListening(false), distantPort(0), distantAddress() {
[15]34 blockOnSend = _blockOnSend;
35 blockOnReceive = _blockOnReceive;
[2]36}
37
38TcpSocket::~TcpSocket() {
[15]39 // Info("Debug: destroying TCP socket %s", ObjectName().c_str());
[238]40 if (socket) close(socket);
[2]41}
42
[15]43void TcpSocket::Listen(const unsigned int port,
44 const std::string localAddress) {
45 socket = ::socket(AF_INET, SOCK_STREAM, 0);
[2]46
[15]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);
[2]56
[15]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 }
[2]61
[238]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 }
[2]69}
70
71TcpSocket *TcpSocket::Accept(Time timeout) {
[238]72 if (!isListening) throw std::logic_error("Can't call Accept on a non listening socket");
73
[15]74 TcpSocket *acceptedSocket = nullptr;
[2]75
[15]76 struct timeval tv;
77 if (timeout != 0) {
[238]78 tv.tv_sec = timeout / 1000000000;
79 tv.tv_usec = (timeout % 1000000000) / 1000;
[15]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) {
[213]87 char errorMsg[256];
[15]88 Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
89 } else {
90 if (ret == 0) {
91 // timeout reached
[238]92 throw std::runtime_error("Timeout");
[2]93 } else {
[15]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);
[238]99 ret = accept(socket, (sockaddr *)&their_addr, &namelen);
100 if (ret < 0) {
[15]101 delete acceptedSocket;
[238]102 throw std::system_error(std::error_code(ret, std::generic_category()));
103 } else {
104 acceptedSocket->socket = ret;
[15]105 }
[2]106 }
[15]107 }
[2]108
[15]109 return acceptedSocket;
[2]110}
111
[238]112bool TcpSocket::Connect(const unsigned int _distantPort,
113 const std::string _distantAddress, Time timeout) {
[15]114 bool success = false;
[238]115 bool criticalError=false; //meaning: if true then this socket can't be re-used
[2]116
[238]117 //should we open a new socket, close it, or re-use it?
[15]118 if (isConnected) {
[238]119 // if already connected to the same host, just return true
120 if (distantPort == _distantPort && distantAddress == _distantAddress) {
[15]121 return true;
[238]122 }
123 // if already connected, but to a different host, close the current socket
124 else {
125 isConnected=false;
[15]126 close(socket);
[238]127 socket=0;
[2]128 }
[15]129 }
[238]130 if (!socket) {
131 socket = ::socket(AF_INET, SOCK_STREAM, 0);
132 if (socket == -1)
133 return false;
134 }
[2]135
[15]136 sockaddr_in serv_addr;
137 serv_addr.sin_family = AF_INET;
[241]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");
[15]141 close(socket);
[238]142 socket=0;
[15]143 return false;
144 }
145 memset(&(serv_addr.sin_zero), '\0', 8);
[241]146 distantPort=_distantPort;
147 distantAddress=_distantAddress;
[2]148
[15]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)) {
[213]154 char errorMsg[256];
[15]155 Err("socket connect: %s\n",
156 strerror_r(errno, errorMsg, sizeof(errorMsg)));
[238]157 criticalError = true;
[15]158 } else {
159 // now block with a timeout
160 struct timeval tv;
[230]161 if (timeout != 0) {// timeout is in ns
162 tv.tv_sec = timeout /((Time)1000000000);
163 tv.tv_usec = (timeout%((Time)1000000000))/1000;
[15]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) {
[213]172 char errorMsg[256];
[15]173 Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
[238]174 criticalError = true;
[15]175 } else {
[238]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
[15]178 int error;
179 socklen_t len = sizeof(error);
180 if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
[213]181 //char errorMsg[256];
182 // Err("getsockopt: %s\n",strerror_r(errno,errorMsg,sizeof(errorMsg)));
[238]183 criticalError = true;
[15]184 } else if (error != 0) {
[213]185 //char errorMsg[256];
186 // Err("socket error: %d(%s)\n",error,strerror_r(error,errorMsg,sizeof(errorMsg)));
[238]187 criticalError = true;
[15]188 } else {
[238]189 if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) != -1) {
[15]190 // Info("connected indeed ^^\n");
191 success = true;
[238]192 } else {
193 //connect failed
194 if ((errno != EINPROGRESS) && (errno != EAGAIN)) {
195 criticalError = true;
196 }
[2]197 }
[15]198 }
[2]199 }
[15]200 }
[2]201 }
[15]202 } else {
[238]203 success = true; // should never happen since we go non blocking and connect can't be immediate
[15]204 }
205 // switch back to blocking mode (default)
206 fcntl(socket, F_SETFL, flags);
[2]207
[15]208 if (!success) {
[238]209 if (criticalError) {
210 close(socket);
211 socket=0;
212 }
213 isConnected=false;
[230]214 //Info("Debug: Connect to %s:%d failed\n", distantAddress.c_str(), distantPort);
[15]215 return false;
216 } else {
217 isConnected = true;
[238]218 distantPort = _distantPort;
219 distantAddress = _distantAddress;
[230]220 //Info("Debug: Connect to %s:%d succeeded\n", distantAddress.c_str(), distantPort);
[15]221 return true;
222 }
[2]223}
224
[15]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;
[238]232 tv.tv_sec = timeout / 1000000000; // timeout is in ns
233 tv.tv_usec = (timeout % 1000000000) / 1000;
[2]234
[15]235 setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,
236 sizeof(struct timeval));
237 }
238 ssize_t bytesSent = send(socket, buffer, bufferSize, flags);
[2]239
[15]240 return bytesSent;
[2]241}
242
[15]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;
[238]249 tv.tv_sec = timeout / 1000000000; // timeout is in ns
250 tv.tv_usec = (timeout % 1000000000) / 1000;
[2]251
[15]252 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
253 sizeof(struct timeval));
254 }
255 ssize_t bytesRead = recv(socket, buffer, bufferSize, flags);
[2]256
[15]257 return bytesRead;
[2]258}
259
[15]260uint16_t TcpSocket::NetworkToHost16(uint16_t data) { return ntohs(data); }
[2]261
[15]262uint16_t TcpSocket::HostToNetwork16(uint16_t data) { return htons(data); }
[2]263
[15]264uint32_t TcpSocket::NetworkToHost32(uint32_t data) { return ntohl(data); }
[2]265
[15]266uint32_t TcpSocket::HostToNetwork32(uint32_t data) { return htonl(data); }
[2]267
268} // end namespace core
269} // end namespace flair
Note: See TracBrowser for help on using the repository browser.