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