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