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