]> git.tdb.fi Git - libs/net.git/blob - source/socket.cpp
0e2950930f0e653d7348bcff826d3b0d53fcb57c
[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 "sockaddr_private.h"
13 #include "socket.h"
14 #include "socket_private.h"
15
16 namespace {
17
18 #ifdef WIN32
19 class WinSockHelper
20 {
21 public:
22         WinSockHelper()
23         {
24                 WSADATA wsa_data;
25                 int err = WSAStartup(0x0002, &wsa_data);
26                 if(err)
27                         std::cerr<<"Failed to initialize WinSock: "<<err<<'\n';
28         }
29
30         ~WinSockHelper()
31         {
32                 WSACleanup();
33         }
34 };
35
36 WinSockHelper wsh;
37 #endif
38
39 }
40
41 namespace Msp {
42 namespace Net {
43
44 Socket::Socket(const Private &p, const SockAddr &paddr):
45         priv(new Private),
46         connected(true),
47         local_addr(0),
48         peer_addr(paddr.copy())
49 {
50         priv->handle = p.handle;
51
52         SockAddr::SysAddr sa;
53         getsockname(priv->handle, reinterpret_cast<sockaddr *>(&sa.addr), &sa.size);
54         local_addr = SockAddr::from_sys(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(family_to_sys(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::SysAddr sa = addr.to_sys();
117
118         int err = ::bind(priv->handle, reinterpret_cast<sockaddr *>(&sa.addr), sa.size);
119         if(err==-1)
120                 throw system_error("bind");
121
122         delete local_addr;
123         local_addr = addr.copy();
124 }
125
126 void Socket::set_timeout(const Time::TimeDelta &timeout)
127 {
128 #ifndef WIN32
129         timeval tv = Time::rawtime_to_timeval(timeout.raw());
130         set_option(SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(timeval));
131         set_option(SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(timeval));
132 #else
133         DWORD msecs = static_cast<DWORD>(timeout/Time::msec);
134         set_option(SOL_SOCKET, SO_RCVTIMEO, &msecs, sizeof(DWORD));
135         set_option(SOL_SOCKET, SO_SNDTIMEO, &msecs, sizeof(DWORD));
136 #endif
137 }
138
139 const SockAddr &Socket::get_local_address() const
140 {
141         if(local_addr==0)
142                 throw bad_socket_state("not bound");
143         return *local_addr;
144 }
145
146 const SockAddr &Socket::get_peer_address() const
147 {
148         if(peer_addr==0)
149                 throw bad_socket_state("not connected");
150         return *peer_addr;
151 }
152
153 int Socket::set_option(int level, int optname, const void *optval, socklen_t optlen)
154 {
155 #ifdef WIN32
156         return setsockopt(priv->handle, level, optname, reinterpret_cast<const char *>(optval), optlen);
157 #else
158         return setsockopt(priv->handle, level, optname, optval, optlen);
159 #endif
160 }
161
162 int Socket::get_option(int level, int optname, void *optval, socklen_t *optlen) const
163 {
164 #ifdef WIN32
165         return getsockopt(priv->handle, level, optname, reinterpret_cast<char *>(optval), optlen);
166 #else
167         return getsockopt(priv->handle, level, optname, optval, optlen);
168 #endif
169 }
170
171 unsigned Socket::do_write(const char *buf, unsigned size)
172 {
173         if(!connected)
174                 throw bad_socket_state("not connected");
175
176         if(size==0)
177                 return 0;
178
179         int ret = ::send(priv->handle, buf, size, 0);
180         if(ret<0)
181         {
182                 if(errno==EAGAIN)
183                         return 0;
184                 else
185                         throw system_error("send");
186         }
187
188         return ret;
189 }
190
191 unsigned Socket::do_read(char *buf, unsigned size)
192 {
193         if(!connected)
194                 throw bad_socket_state("not connected");
195
196
197         if(size==0)
198                 return 0;
199
200         int ret = ::recv(priv->handle, buf, size, 0);
201         if(ret<0)
202         {
203                 if(errno==EAGAIN)
204                         return 0;
205                 else
206                         throw system_error("recv");
207         }
208         else if(ret==0 && !eof_flag)
209         {
210                 eof_flag = true;
211                 signal_end_of_file.emit();
212                 set_events(IO::P_NONE);
213         }
214
215         return ret;
216 }
217
218 } // namespace Net
219 } // namespace Msp