]> git.tdb.fi Git - libs/net.git/blob - source/socket.cpp
Add function to check if handshake is done
[libs/net.git] / source / socket.cpp
1 /* $Id$
2
3 This file is part of libmspnet
4 Copyright © 2008, 2010-2011  Mikkosoft Productions, Mikko Rasa
5 Distributed under the LGPL
6 */
7
8 #ifndef WIN32
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/socket.h>
12 #endif
13 #include <iostream>
14 #include <msp/strings/formatter.h>
15 #include <msp/time/units.h>
16 #include "socket.h"
17
18 namespace {
19
20 #ifdef WIN32
21 class WinSockHelper
22 {
23 public:
24         WinSockHelper()
25         {
26                 WSADATA wsa_data;
27                 int err=WSAStartup(0x0002, &wsa_data);
28                 if(err)
29                         std::cerr<<"Failed to initialize WinSock: "<<err<<'\n';
30         }
31
32         ~WinSockHelper()
33         {
34                 WSACleanup();
35         }
36 };
37
38 WinSockHelper wsh;
39 #endif
40
41 }
42
43 namespace Msp {
44 namespace Net {
45
46 Socket::Socket(SocketHandle h, const SockAddr &paddr):
47         handle(h),
48         connected(true),
49         local_addr(0),
50         peer_addr(paddr.copy())
51 {
52         sockaddr_storage sa;
53         socklen_t size=sizeof(sockaddr_storage);
54         getsockname(handle, reinterpret_cast<sockaddr *>(&sa), &size);
55         local_addr=SockAddr::create(sa);
56
57 #ifdef WIN32
58         event=CreateEvent(0, false, false, 0);
59 #endif
60 }
61
62 Socket::Socket(Family af, int type, int proto):
63         connected(false),
64         local_addr(0),
65         peer_addr(0)
66 {
67         handle=socket(af, type, proto);
68
69 #ifdef WIN32
70         event=CreateEvent(0, false, false, 0);
71 #endif
72 }
73
74 Socket::~Socket()
75 {
76         close();
77 }
78
79 void Socket::set_block(bool b)
80 {
81         mode=(mode&~IO::M_NONBLOCK);
82         if(b)
83                 mode=(mode|IO::M_NONBLOCK);
84
85 #ifdef WIN32
86         u_long flag=!b;
87         ioctlsocket(handle, FIONBIO, &flag);
88 #else
89         int flags=fcntl(handle, F_GETFL);
90         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
91 #endif
92 }
93
94 IO::Handle Socket::get_event_handle()
95 {
96 #ifdef WIN32
97         return event;
98 #else
99         return handle;
100 #endif
101 }
102
103
104 void Socket::bind(const SockAddr &addr)
105 {
106         check_state(false);
107
108         sockaddr_storage sa;
109         unsigned size=addr.fill_sockaddr(sa);
110
111         int err=::bind(handle, reinterpret_cast<sockaddr *>(&sa), size);
112         if(err==-1)
113                 throw SystemError("Unable to bind", errno);
114
115         delete local_addr;
116         local_addr=addr.copy();
117 }
118
119 /**
120 Closes the socket.  Most operations on the socket will throw an exception after
121 this.
122 */
123 void Socket::close()
124 {
125         if(handle==MSP_NET_INVALID_SOCKET_HANDLE)
126                 return;
127
128         set_events(IO::P_NONE);
129
130         signal_flush_required.emit();
131 #ifdef WIN32
132         closesocket(handle);
133         CloseHandle(event);
134 #else
135         ::close(handle);
136 #endif
137         handle=MSP_NET_INVALID_SOCKET_HANDLE;
138         connected=false;
139         signal_closed.emit();
140
141         delete local_addr;
142         local_addr=0;
143         delete peer_addr;
144         peer_addr=0;
145 }
146
147 void Socket::set_timeout(const Time::TimeDelta &timeout)
148 {
149 #ifndef WIN32
150         timeval tv;
151         timeout.fill_timeval(tv);
152         set_option(SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));
153         set_option(SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval));
154 #else
155         DWORD msecs = static_cast<DWORD>(timeout/Time::msec);
156         set_option(SOL_SOCKET, SO_RCVTIMEO, &msecs, sizeof(DWORD));
157         set_option(SOL_SOCKET, SO_SNDTIMEO, &msecs, sizeof(DWORD));
158 #endif
159 }
160
161 const SockAddr &Socket::get_local_address() const
162 {
163         if(local_addr==0)
164                 throw InvalidState("Local address not set");
165         return *local_addr;
166 }
167
168 const SockAddr &Socket::get_peer_address() const
169 {
170         if(peer_addr==0)
171                 throw InvalidState("Peer address not set");
172         return *peer_addr;
173 }
174
175 void Socket::check_state(bool conn) const
176 {
177         if(handle==MSP_NET_INVALID_SOCKET_HANDLE)
178                 throw Exception("Socket is closed");
179         if(conn && !connected)
180                 throw Exception("Socket is not connected");
181 }
182
183 int Socket::set_option(int level, int optname, const void *optval, socklen_t optlen)
184 {
185 #ifdef WIN32
186         return setsockopt(handle, level, optname, reinterpret_cast<const char *>(optval), optlen);
187 #else
188         return setsockopt(handle, level, optname, optval, optlen);
189 #endif
190 }
191
192 int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen) const
193 {
194 #ifdef WIN32
195         return getsockopt(handle, level, optname, reinterpret_cast<char *>(optval), optlen);
196 #else
197         return getsockopt(handle, level, optname, optval, optlen);
198 #endif
199 }
200
201 unsigned Socket::do_write(const char *buf, unsigned size)
202 {
203         check_state(true);
204
205         if(size==0)
206                 return 0;
207
208         int ret=::send(handle, buf, size, 0);
209         if(ret<0)
210         {
211                 if(errno==EAGAIN)
212                         return 0;
213                 else
214                         throw SystemError("Writing to socket failed", errno);
215         }
216
217         return ret;
218 }
219
220 unsigned Socket::do_read(char *buf, unsigned size)
221 {
222         check_state(true);
223
224         if(size==0)
225                 return 0;
226
227         int ret=::recv(handle, buf, size, 0);
228         if(ret<0)
229         {
230                 if(errno==EAGAIN)
231                         return 0;
232                 else
233                         throw SystemError("Reading from socket failed", errno);
234         }
235         else if(ret==0 && !eof_flag)
236         {
237                 eof_flag=true;
238                 signal_end_of_file.emit();
239                 set_events(IO::P_NONE);
240         }
241
242         return ret;
243 }
244
245 } // namespace Net
246 } // namespace Msp