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