]> git.tdb.fi Git - libs/net.git/blob - source/streamsocket.cpp
Add function to check if handshake is done
[libs/net.git] / source / streamsocket.cpp
1 /* $Id$
2
3 This file is part of libmspnet
4 Copyright © 2008-2009, 2011  Mikkosoft Productions, Mikko Rasa
5 Distributed under the LGPL
6 */
7
8 #ifndef WIN32
9 #include <sys/socket.h>
10 #endif
11 #include <cerrno>
12 #include <msp/io/poll.h>
13 #include <msp/strings/formatter.h>
14 #include "streamsocket.h"
15
16 namespace Msp {
17 namespace Net {
18
19 /**
20 Used by StreamListenSocket to construct a new socket from accept.
21 */
22 StreamSocket::StreamSocket(SocketHandle h, const SockAddr &paddr):
23         Socket(h, paddr),
24         connecting(false)
25 {
26 #ifdef WIN32
27         WSAEventSelect(handle, event, FD_READ|FD_CLOSE);
28 #endif
29         set_events(IO::P_INPUT);
30 }
31
32 /**
33 Constructs a new StreamSocket.
34 */
35 StreamSocket::StreamSocket(Family af, int proto):
36         Socket(af, SOCK_STREAM, proto),
37         connecting(false)
38 { }
39
40 /**
41 Checks the status of an ongoing connection attempt.  If the connection fails
42 with an error, an exception is thrown.
43
44 @return  0 if the connection finished, 1 if not
45 */
46 int StreamSocket::poll_connect(const Time::TimeDelta &timeout)
47 {
48         check_state(false);
49         if(!connecting)
50                 throw InvalidState("No connection attempt going on");
51
52         int res=poll(*this, IO::P_OUTPUT, timeout);
53         if(res==-1)
54 #ifdef WIN32
55                 throw Exception(format("Connection polling failed: %d", WSAGetLastError()));
56 #else
57                 throw SystemError("Connection polling failed", errno);
58 #endif
59         else if(res>0)
60         {
61                 connecting=false;
62
63                 int err;
64                 socklen_t len=sizeof(int);
65                 get_option(SOL_SOCKET, SO_ERROR, &err, &len);
66
67                 if(err!=0)
68                 {
69                         set_events(IO::P_NONE);
70 #ifdef WIN32
71                         throw Exception(format("Connection failed: %d", err));
72 #else
73                         throw SystemError("Connection failed", err);
74 #endif
75                 }
76
77 #ifdef WIN32
78                 WSAEventSelect(handle, event, FD_READ|FD_CLOSE);
79 #endif
80                 set_events(IO::P_INPUT);
81
82                 connected=true;
83
84                 return 0;
85         }
86
87         return 1;
88 }
89
90 /**
91 Connects the socket to a remote address.  In non-blocking mode, this function
92 may return before the connection is finished.  The caller must then use either
93 the poll_connect function or an EventDispatcher to determine when the
94 connection is finished.
95
96 @return  0 if the connection finished, 1 if it is in progress
97 */
98 int StreamSocket::connect(const SockAddr &addr)
99 {
100         check_state(false);
101
102         if(connected)
103                 throw InvalidState("Socket is already connected");
104
105         sockaddr_storage sa;
106         socklen_t size=addr.fill_sockaddr(sa);
107
108 #ifdef WIN32
109         int err=WSAConnect(handle, reinterpret_cast<sockaddr *>(&sa), size, 0, 0, 0, 0);
110         if(err==SOCKET_ERROR)
111         {
112                 int err_code=WSAGetLastError();
113                 if(err_code==WSAEWOULDBLOCK)
114                 {
115                         connecting=true;
116                         WSAEventSelect(handle, event, FD_CONNECT);
117                         set_events(IO::P_OUTPUT);
118                 }
119                 else
120                         throw Exception(format("Unable to connect: %d", err_code));
121         }
122 #else
123         int err=::connect(handle, reinterpret_cast<sockaddr *>(&sa), size);
124         if(err==-1)
125         {
126                 if(errno==EINPROGRESS)
127                 {
128                         connecting=true;
129                         set_events(IO::P_OUTPUT);
130                 }
131                 else
132                         throw SystemError("Unable to connect", errno);
133         }
134 #endif
135
136         delete peer_addr;
137         peer_addr=addr.copy();
138
139         delete local_addr;
140         size=sizeof(sockaddr_storage);
141         getsockname(handle, reinterpret_cast<sockaddr *>(&sa), &size);
142         local_addr=SockAddr::create(sa);
143
144         if(err==0)
145         {
146                 connected=true;
147                 set_events(IO::P_INPUT);
148                 signal_connect_finished.emit(0);
149         }
150
151         return (err==0)?0:1;
152 }
153
154 void StreamSocket::on_event(IO::PollEvent ev)
155 {
156         //cout<<"StreamSocket::on_event "<<ev<<'\n';
157         if((ev&(IO::P_OUTPUT|IO::P_ERROR)) && connecting)
158         {
159                 int err;
160                 socklen_t len=sizeof(err);
161                 get_option(SOL_SOCKET, SO_ERROR, &err, &len);
162
163                 connecting=false;
164                 connected=(err==0);
165                 signal_connect_finished.emit(err);
166
167                 if(err!=0)
168                 {
169                         delete peer_addr;
170                         peer_addr=0;
171                 }
172
173 #ifdef WIN32
174                 WSAEventSelect(handle, event, FD_READ|FD_CLOSE);
175 #endif
176                 set_events((err==0) ? IO::P_INPUT : IO::P_NONE);
177         }
178 }
179
180 } // namespace Net
181 } // namespace Msp