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