better socket sending/receiving
[chaz/yoink] / src / Moof / Socket.hh
1
2 /*] Copyright (c) 2009-2010, Charles McGarvey [**************************
3 **] All rights reserved.
4 *
5 * vi:ts=4 sw=4 tw=75
6 *
7 * Distributable under the terms and conditions of the 2-clause BSD license;
8 * see the file COPYING for a complete text of the license.
9 *
10 **************************************************************************/
11
12 #ifndef _MOOF_SOCKET_HH_
13 #define _MOOF_SOCKET_HH_
14
15 #include <cstring>
16 #include <sstream>
17 #include <string>
18 #include <vector>
19
20 #if defined(_WIN32)
21 #include <winsock2.h>
22 #include <ws2tcpip.h>
23 #include <wspiapi.h>
24 #else
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #if HAVE_FCNTL_H
30 #include <fcntl.h>
31 #else
32 #include <sys/ioctl.h>
33 #endif
34 #endif
35
36 #include <Moof/Log.hh>
37 #include <Moof/Packet.hh>
38 #include <Moof/Thread.hh>
39
40
41 #ifndef SO_NONBLOCK
42 #define SO_NONBLOCK 1024
43 #endif
44
45
46 namespace Mf {
47
48
49 class SocketAddress
50 {
51 public:
52
53 SocketAddress() :
54 mSize(0),
55 mType(0)
56 {
57 mAddress.sa.sa_family = AF_UNSPEC;
58 mAddress.v4.sin_port = 0;
59 }
60
61 SocketAddress(const std::string& service, const std::string& name,
62 int type = SOCK_STREAM, int family = AF_UNSPEC)
63 {
64 init(service, name, type, family);
65 }
66
67 SocketAddress(const std::string& service,
68 int type = SOCK_STREAM, int family = AF_UNSPEC)
69 {
70 init(service, type, family);
71 }
72
73 SocketAddress(const struct addrinfo* addr, const std::string& name)
74 {
75 mType = addr->ai_socktype;
76 memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen);
77 mName = name;
78 mSize = addr->ai_addrlen;
79 }
80
81 SocketAddress(const struct sockaddr* addr, size_t size,
82 int type = SOCK_STREAM)
83 {
84 mType = type;
85 memcpy(&mAddress.sa, addr, size);
86 mSize = size;
87 setNameFromAddress();
88 }
89
90
91 static SocketAddress broadcast(const std::string& service)
92 {
93 std::istringstream stream(service);
94 unsigned short port;
95 stream >> port;
96
97 struct sockaddr_in addr;
98 addr.sin_family = AF_INET;
99 addr.sin_port = htons(port);
100 addr.sin_addr.s_addr = INADDR_BROADCAST;
101 memset(&addr.sin_zero, 0, sizeof(addr.sin_zero));
102 return SocketAddress((sockaddr*)&addr, sizeof(addr), SOCK_DGRAM);
103 }
104
105
106 void init(const std::string& service, const std::string& name = "",
107 int type = SOCK_STREAM, int family = AF_UNSPEC)
108 {
109 ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
110 ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
111
112 struct addrinfo hints;
113 memset(&hints, 0, sizeof(hints));
114 hints.ai_family = family;
115 hints.ai_socktype = type;
116 hints.ai_flags = AI_PASSIVE;
117
118 struct addrinfo* addr;
119 int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0,
120 service.c_str(), &hints, &addr);
121 if (status == 0)
122 {
123 mType = addr->ai_socktype;
124 memcpy(&mAddress.sa, addr->ai_addr, addr->ai_addrlen);
125 mSize = addr->ai_addrlen;
126
127 if (name != "") mName = name;
128 else setNameFromAddress();
129
130 freeaddrinfo(addr);
131 }
132 else
133 {
134 Mf::logWarning(gai_strerror(status));
135 mType = 0;
136 mSize = 0;
137 mAddress.sa.sa_family = AF_UNSPEC;
138 mAddress.v4.sin_port = 0;
139 }
140 }
141
142 void init(const std::string& service,
143 int type = SOCK_STREAM, int family = AF_UNSPEC)
144 {
145 init(service, "", type, family);
146 }
147
148
149 const std::string& name() const
150 {
151 return mName;
152 }
153
154 void setName(const std::string& name)
155 {
156 mName = name;
157 }
158
159 unsigned short port() const
160 {
161 return ntohs(mAddress.v4.sin_port);
162 }
163
164 int type() const
165 {
166 return mType;
167 }
168
169 int family() const
170 {
171 return mAddress.sa.sa_family;
172 }
173
174
175 const struct sockaddr* address() const
176 {
177 return mSize != 0 ? &mAddress.sa : 0;
178 }
179
180 size_t size() const
181 {
182 return mSize;
183 }
184
185
186 static int resolve(const std::string& service, const std::string& name,
187 int type, int family, std::vector<SocketAddress>& resolved)
188 {
189 ASSERT(type == SOCK_STREAM || type == SOCK_DGRAM);
190 ASSERT(family == AF_INET || family == AF_INET6 || family == AF_UNSPEC);
191
192 resolved.clear();
193
194 struct addrinfo hints;
195 memset(&hints, 0, sizeof(hints));
196 hints.ai_family = family;
197 hints.ai_socktype = type;
198 hints.ai_flags = AI_PASSIVE;
199
200 struct addrinfo* list;
201 int status = getaddrinfo(name.length() > 0 ? name.c_str() : 0,
202 service.length() > 0 ? service.c_str() : 0, &hints, &list);
203 if (status == 0)
204 {
205 for (struct addrinfo* addr = list;
206 addr != 0; addr = addr->ai_next)
207 {
208 resolved.push_back(SocketAddress(addr, name));
209 }
210
211 freeaddrinfo(list);
212 }
213 else
214 {
215 Mf::logWarning(gai_strerror(status));
216 return -1;
217 }
218
219 return 0;
220 }
221
222
223 private:
224
225 void setNameFromAddress()
226 {
227 #if defined(_WIN32)
228 // inet_ntop was introduced in Vista
229 mName = inet_ntoa(mAddress.v4.sin_addr);
230 #else
231 char name[INET6_ADDRSTRLEN] = {'\0'};
232 inet_ntop(mAddress.sa.sa_family, &mAddress.sa, name, sizeof(name));
233 mName = name;
234 #endif
235 }
236
237
238 union
239 {
240 sockaddr sa;
241 sockaddr_in v4;
242 sockaddr_in6 v6;
243 sockaddr_storage storage;
244 } mAddress;
245 size_t mSize;
246 std::string mName;
247 int mType;
248 };
249
250
251 class Socket
252 {
253 public:
254
255 Socket(const SocketAddress& address) :
256 mFd(-1),
257 mIsConnected(false),
258 mAddress(address)
259 {
260 mFd = socket(address.family(), address.type(), 0);
261 }
262
263 ~Socket()
264 {
265 #if defined(_WIN32)
266 if (mFd != -1) closesocket(mFd);
267 #else
268 if (mFd != -1) close(mFd);
269 #endif
270 }
271
272
273 bool isConnected() const
274 {
275 return mIsConnected;
276 }
277
278 const SocketAddress& address() const
279 {
280 return mAddress;
281 }
282
283
284 int connect()
285 {
286 int result = ::connect(mFd, mAddress.address(), mAddress.size());
287 mIsConnected = result != -1;
288 return result;
289 }
290
291 int bind()
292 {
293 return ::bind(mFd, mAddress.address(), mAddress.size());
294 }
295
296 int listen(int backlog = SOMAXCONN)
297 {
298 return ::listen(mFd, backlog > 0 ? backlog : SOMAXCONN);
299 }
300
301 Socket accept()
302 {
303 return Socket(mFd);
304 }
305
306
307 int set(int option, int value = 0)
308 {
309 if (option == SO_NONBLOCK)
310 {
311 #ifdef HAVE_FCNTL
312 int flags = fcntl(mFd, F_GETFL);
313 return fcntl(mFd, F_SETFL, (value ? O_NONBLOCK : 0) | flags);
314 #else
315 return ioctl(mFd, FIONBIO, value);
316 #endif
317 }
318 return setsockopt(mFd, SOL_SOCKET, option, &value, sizeof(value));
319 }
320
321 int set(int option, const std::string& value)
322 {
323 return setsockopt(mFd, SOL_SOCKET, option,
324 value.data(), value.length());
325 }
326
327 int get(int option, int& value)
328 {
329 if (option == SO_NONBLOCK)
330 {
331 #ifdef HAVE_FCNTL
332 int flags = fcntl(mFd, F_GETFL);
333 return flags & O_NONBLOCK;
334 #else
335 return ioctl(mFd, FIONBIO, &value);
336 #endif
337 }
338 socklen_t optlen = sizeof(value);
339 return getsockopt(mFd, SOL_SOCKET, option, &value, &optlen);
340 }
341
342 int get(int option, std::string& value)
343 {
344 char str[64] = {'\0'};
345 socklen_t optlen = sizeof(str);
346 int result = getsockopt(mFd, SOL_SOCKET, option, &str, &optlen);
347 value = str;
348 return result;
349 }
350
351
352 ssize_t write(const void* bytes, size_t size)
353 {
354 return send(mFd, bytes, size, 0);
355 }
356 ssize_t write(const void* bytes, size_t size,
357 const SocketAddress& address)
358 {
359 return sendto(mFd, bytes, size, 0,
360 address.address(), address.size());
361 }
362
363 ssize_t write(const Packet& packet)
364 {
365 return write(packet.bytes(), packet.size());
366 }
367
368 ssize_t write(const Packet& packet, const SocketAddress& address)
369 {
370 return write(packet.bytes(), packet.size(), address);
371 }
372
373
374 ssize_t read(void* bytes, size_t size)
375 {
376 return recv(mFd, bytes, size, 0);
377 }
378
379 ssize_t read(void* bytes, size_t size, SocketAddress& address)
380 {
381 union
382 {
383 sockaddr sa;
384 sockaddr_storage storage;
385 } addr;
386 socklen_t length = sizeof(addr);
387
388 ssize_t result = recvfrom(mFd, bytes, size, 0, &addr.sa, &length);
389 if (result != -1)
390 {
391 address = SocketAddress(&addr.sa, length, mAddress.type());
392 }
393 return result;
394 }
395
396 ssize_t read(Packet& packet)
397 {
398 char buffer[65536];
399 ssize_t result = read(buffer, sizeof(buffer));
400 if (result != -1) packet = Packet(buffer, result);
401 return result;
402 }
403
404 ssize_t read(Packet& packet, SocketAddress& address)
405 {
406 char buffer[65536];
407 ssize_t result = read(buffer, sizeof(buffer), address);
408 if (result != -1) packet = Packet(buffer, result);
409 return result;
410 }
411
412
413 private:
414
415 Socket(int fd)
416 {
417 // for accepting a socket from fd
418 union
419 {
420 sockaddr sa;
421 sockaddr_storage storage;
422 } addr;
423 socklen_t length = sizeof(addr);
424
425 mFd = ::accept(fd, &addr.sa, &length);
426 mAddress = SocketAddress(&addr.sa, length);
427 }
428
429
430 int mFd;
431 bool mIsConnected;
432 SocketAddress mAddress;
433 };
434
435
436 class ResolverTask : public ThreadedTask
437 {
438 public:
439
440 ResolverTask(const std::string& service, const std::string& name,
441 int type = SOCK_STREAM, int family = AF_UNSPEC) :
442 mIsDone(false)
443 {
444 mFunction = boost::bind(&ResolverTask::resolve,
445 this, service, name, type, family);
446 }
447
448
449 bool isDone() const
450 {
451 return mIsDone;
452 }
453
454 void run()
455 {
456 if (!mThread) mThread = Mf::detachFunction(mFunction);
457 }
458
459
460 const std::vector<SocketAddress>& addresses() const
461 {
462 return mAddressList;
463 }
464
465
466 private:
467
468 int resolve(const std::string& service, const std::string& name,
469 int type, int family)
470 {
471 int status = SocketAddress::resolve(service, name,
472 type, family, mAddressList);
473 mIsDone = true;
474 return status;
475 }
476
477
478 std::vector<SocketAddress> mAddressList;
479 bool mIsDone;
480 Function mFunction;
481 };
482
483
484 } // namespace Mf
485
486 #endif // _MOOF_SOCKET_HH_
487
This page took 0.051234 seconds and 4 git commands to generate.