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