]> git.tdb.fi Git - libs/net.git/blob - source/socket.cpp
Remove the close method from Socket, making it more RAII
[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         signal_flush_required.emit();
77 #ifdef WIN32
78         closesocket(handle);
79         CloseHandle(event);
80 #else
81         ::close(handle);
82 #endif
83
84         delete local_addr;
85         delete peer_addr;
86 }
87
88 void Socket::set_block(bool b)
89 {
90         mode = (mode&~IO::M_NONBLOCK);
91         if(b)
92                 mode = (mode|IO::M_NONBLOCK);
93
94 #ifdef WIN32
95         u_long flag = !b;
96         ioctlsocket(handle, FIONBIO, &flag);
97 #else
98         int flags = fcntl(handle, F_GETFL);
99         fcntl(handle, F_SETFL, (flags&O_NONBLOCK)|(b?0:O_NONBLOCK));
100 #endif
101 }
102
103 const IO::Handle &Socket::get_event_handle()
104 {
105         return event;
106 }
107
108
109 void Socket::bind(const SockAddr &addr)
110 {
111         check_state(false);
112
113         sockaddr_storage sa;
114         unsigned size = addr.fill_sockaddr(sa);
115
116         int err = ::bind(handle, reinterpret_cast<sockaddr *>(&sa), size);
117         if(err==-1)
118                 throw system_error("bind");
119
120         delete local_addr;
121         local_addr = addr.copy();
122 }
123
124 void Socket::set_timeout(const Time::TimeDelta &timeout)
125 {
126 #ifndef WIN32
127         timeval tv = Time::rawtime_to_timeval(timeout.raw());
128         set_option(SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));
129         set_option(SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval));
130 #else
131         DWORD msecs = static_cast<DWORD>(timeout/Time::msec);
132         set_option(SOL_SOCKET, SO_RCVTIMEO, &msecs, sizeof(DWORD));
133         set_option(SOL_SOCKET, SO_SNDTIMEO, &msecs, sizeof(DWORD));
134 #endif
135 }
136
137 const SockAddr &Socket::get_local_address() const
138 {
139         if(local_addr==0)
140                 throw bad_socket_state("not bound");
141         return *local_addr;
142 }
143
144 const SockAddr &Socket::get_peer_address() const
145 {
146         if(peer_addr==0)
147                 throw bad_socket_state("not connected");
148         return *peer_addr;
149 }
150
151 void Socket::check_state(bool conn) const
152 {
153         if(conn && !connected)
154                 throw bad_socket_state("not connected");
155 }
156
157 int Socket::set_option(int level, int optname, const void *optval, socklen_t optlen)
158 {
159 #ifdef WIN32
160         return setsockopt(handle, level, optname, reinterpret_cast<const char *>(optval), optlen);
161 #else
162         return setsockopt(handle, level, optname, optval, optlen);
163 #endif
164 }
165
166 int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen) const
167 {
168 #ifdef WIN32
169         return getsockopt(handle, level, optname, reinterpret_cast<char *>(optval), optlen);
170 #else
171         return getsockopt(handle, level, optname, optval, optlen);
172 #endif
173 }
174
175 unsigned Socket::do_write(const char *buf, unsigned size)
176 {
177         check_state(true);
178
179         if(size==0)
180                 return 0;
181
182         int ret = ::send(handle, buf, size, 0);
183         if(ret<0)
184         {
185                 if(errno==EAGAIN)
186                         return 0;
187                 else
188                         throw system_error("send");
189         }
190
191         return ret;
192 }
193
194 unsigned Socket::do_read(char *buf, unsigned size)
195 {
196         check_state(true);
197
198         if(size==0)
199                 return 0;
200
201         int ret = ::recv(handle, buf, size, 0);
202         if(ret<0)
203         {
204                 if(errno==EAGAIN)
205                         return 0;
206                 else
207                         throw system_error("recv");
208         }
209         else if(ret==0 && !eof_flag)
210         {
211                 eof_flag = true;
212                 signal_end_of_file.emit();
213                 set_events(IO::P_NONE);
214         }
215
216         return ret;
217 }
218
219 } // namespace Net
220 } // namespace Msp