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

Last change on this file since 150 was 15, checked in by Bayard Gildas, 9 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.