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

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

correction sémaphore. bloquant tout ça...

File size: 8.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#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 printf("incorrect network address.");
141 close(socket);
142 socket=0;
143 return false;
144 }
145 memset(&(serv_addr.sin_zero), '\0', 8);
146
147 // go non blocking
148 int flags = fcntl(socket, F_GETFL);
149 fcntl(socket, F_SETFL, flags | O_NONBLOCK);
150 if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
151 if ((errno != EINPROGRESS) && (errno != EAGAIN)) {
152 char errorMsg[256];
153 Err("socket connect: %s\n",
154 strerror_r(errno, errorMsg, sizeof(errorMsg)));
155 criticalError = true;
156 } else {
157 // now block with a timeout
158 struct timeval tv;
159 if (timeout != 0) {// timeout is in ns
160 tv.tv_sec = timeout /((Time)1000000000);
161 tv.tv_usec = (timeout%((Time)1000000000))/1000;
162 }
163 fd_set wset;
164 FD_ZERO(&wset);
165 FD_SET(socket, &wset);
166 int ret =
167 select(socket + 1, NULL, &wset, NULL,
168 (timeout == 0) ? NULL : &tv); // man 2 connect EINPROGRESS
169 if (ret < 0) {
170 char errorMsg[256];
171 Err("select: %s\n", strerror_r(errno, errorMsg, sizeof(errorMsg)));
172 criticalError = true;
173 } else {
174 if (ret != 0) { //if ret==0 we're on timeout (nothing to do since success is already set to false)
175 // something happened on our socket. Check if an error occurred
176 int error;
177 socklen_t len = sizeof(error);
178 if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != 0) {
179 //char errorMsg[256];
180 // Err("getsockopt: %s\n",strerror_r(errno,errorMsg,sizeof(errorMsg)));
181 criticalError = true;
182 } else if (error != 0) {
183 //char errorMsg[256];
184 // Err("socket error: %d(%s)\n",error,strerror_r(error,errorMsg,sizeof(errorMsg)));
185 criticalError = true;
186 } else {
187 if (connect(socket, (sockaddr *)&serv_addr, sizeof(serv_addr)) != -1) {
188 // Info("connected indeed ^^\n");
189 success = true;
190 } else {
191 //connect failed
192 if ((errno != EINPROGRESS) && (errno != EAGAIN)) {
193 criticalError = true;
194 }
195 }
196 }
197 }
198 }
199 }
200 } else {
201 success = true; // should never happen since we go non blocking and connect can't be immediate
202 }
203 // switch back to blocking mode (default)
204 fcntl(socket, F_SETFL, flags);
205
206 if (!success) {
207 if (criticalError) {
208 close(socket);
209 socket=0;
210 }
211 isConnected=false;
212 //Info("Debug: Connect to %s:%d failed\n", distantAddress.c_str(), distantPort);
213 return false;
214 } else {
215 isConnected = true;
216 distantPort = _distantPort;
217 distantAddress = _distantAddress;
218 //Info("Debug: Connect to %s:%d succeeded\n", distantAddress.c_str(), distantPort);
219 return true;
220 }
221}
222
223ssize_t TcpSocket::SendMessage(const char *buffer, size_t bufferSize,
224 Time timeout) {
225 int flags = 0;
226 if (!blockOnSend) {
227 flags |= MSG_DONTWAIT;
228 } else {
229 struct timeval tv;
230 tv.tv_sec = timeout / 1000000000; // timeout is in ns
231 tv.tv_usec = (timeout % 1000000000) / 1000;
232
233 setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv,
234 sizeof(struct timeval));
235 }
236 ssize_t bytesSent = send(socket, buffer, bufferSize, flags);
237
238 return bytesSent;
239}
240
241ssize_t TcpSocket::RecvMessage(char *buffer, size_t bufferSize, Time timeout) {
242 int flags = 0;
243 if (!blockOnReceive) {
244 flags |= MSG_DONTWAIT;
245 } else {
246 struct timeval tv;
247 tv.tv_sec = timeout / 1000000000; // timeout is in ns
248 tv.tv_usec = (timeout % 1000000000) / 1000;
249
250 setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
251 sizeof(struct timeval));
252 }
253 ssize_t bytesRead = recv(socket, buffer, bufferSize, flags);
254
255 return bytesRead;
256}
257
258uint16_t TcpSocket::NetworkToHost16(uint16_t data) { return ntohs(data); }
259
260uint16_t TcpSocket::HostToNetwork16(uint16_t data) { return htons(data); }
261
262uint32_t TcpSocket::NetworkToHost32(uint32_t data) { return ntohl(data); }
263
264uint32_t TcpSocket::HostToNetwork32(uint32_t data) { return htonl(data); }
265
266} // end namespace core
267} // end namespace flair
Note: See TracBrowser for help on using the repository browser.