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

Last change on this file since 2 was 2, checked in by Sanahuja Guillaume, 6 years ago

flaircore

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