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

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

bug fix

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.