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