Mercurial > projects > ldc
comparison lphobos/std/socket.d @ 473:373489eeaf90
Applied downs' lphobos update
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Mon, 04 Aug 2008 19:28:49 +0200 |
parents | |
children | 88e23f8c2354 |
comparison
equal
deleted
inserted
replaced
472:15c804b6ce77 | 473:373489eeaf90 |
---|---|
1 // Written in the D programming language | |
2 | |
3 /* | |
4 Copyright (C) 2004-2005 Christopher E. Miller | |
5 | |
6 This software is provided 'as-is', without any express or implied | |
7 warranty. In no event will the authors be held liable for any damages | |
8 arising from the use of this software. | |
9 | |
10 Permission is granted to anyone to use this software for any purpose, | |
11 including commercial applications, and to alter it and redistribute it | |
12 freely, subject to the following restrictions: | |
13 | |
14 1. The origin of this software must not be misrepresented; you must not | |
15 claim that you wrote the original software. If you use this software | |
16 in a product, an acknowledgment in the product documentation would be | |
17 appreciated but is not required. | |
18 2. Altered source versions must be plainly marked as such, and must not | |
19 be misrepresented as being the original software. | |
20 3. This notice may not be removed or altered from any source | |
21 distribution. | |
22 | |
23 socket.d 1.3 | |
24 Jan 2005 | |
25 | |
26 Thanks to Benjamin Herr for his assistance. | |
27 */ | |
28 | |
29 /* NOTE: This file has been patched from the original DMD distribution to | |
30 work with the GDC compiler. | |
31 | |
32 Modified by David Friedman, April 2005 | |
33 */ | |
34 | |
35 /** | |
36 * Notes: For Win32 systems, link with ws2_32.lib. | |
37 * Example: See /dmd/samples/d/listener.d. | |
38 * Authors: Christopher E. Miller | |
39 * Macros: | |
40 * WIKI=Phobos/StdSocket | |
41 */ | |
42 | |
43 module std.socket; | |
44 | |
45 private import std.string, std.stdint, std.c.string, std.c.stdlib; | |
46 | |
47 | |
48 version(Unix) version = BsdSockets; | |
49 | |
50 version (skyos) { /* nothging */ } | |
51 else | |
52 { | |
53 version = have_getservbyport; | |
54 version = have_getprotobynumber; | |
55 } | |
56 | |
57 | |
58 version(Win32) | |
59 { | |
60 private import std.c.windows.windows, std.c.windows.winsock; | |
61 private alias std.c.windows.winsock.timeval _ctimeval; | |
62 | |
63 typedef SOCKET socket_t = INVALID_SOCKET; | |
64 private const int _SOCKET_ERROR = SOCKET_ERROR; | |
65 | |
66 | |
67 private int _lasterr() | |
68 { | |
69 return WSAGetLastError(); | |
70 } | |
71 } | |
72 else version(BsdSockets) | |
73 { | |
74 version (Unix) | |
75 { | |
76 version(linux) { | |
77 import std.c.linux.socket, std.c.linux.linux; | |
78 const AF_UNSPEC=0, AF_UNIX=1, AF_INET=2, AF_IPX=4, AF_APPLETALK=5; | |
79 const MSG_NOSIGNAL=16384; | |
80 const FD_SETSIZE=64; | |
81 alias std.c.linux.linux.timeval _ctimeval; | |
82 } else { | |
83 import std.c.unix.unix; | |
84 alias std.c.unix.unix.timeval _ctimeval; | |
85 } | |
86 } | |
87 | |
88 typedef int32_t socket_t = -1; | |
89 private const int _SOCKET_ERROR = -1; | |
90 | |
91 | |
92 private int _lasterr() | |
93 { | |
94 return getErrno(); | |
95 } | |
96 } | |
97 else | |
98 { | |
99 static assert(0); // No socket support yet. | |
100 } | |
101 | |
102 | |
103 extern(C) char* strerror_r(int errnum, char* buf, size_t buflen); | |
104 /// Base exception thrown from a Socket. | |
105 class SocketException: Exception | |
106 { | |
107 int errorCode; /// Platform-specific error code. | |
108 | |
109 this(string msg, int err = 0) | |
110 { | |
111 errorCode = err; | |
112 | |
113 version(Unix) | |
114 { | |
115 if(errorCode > 0) | |
116 { | |
117 char[80] buf; | |
118 auto cs = strerror_r(errorCode, buf.ptr, buf.length); | |
119 auto len = strlen(cs); | |
120 | |
121 if(cs[len - 1] == '\n') | |
122 len--; | |
123 if(cs[len - 1] == '\r') | |
124 len--; | |
125 msg = msg ~ ": " ~ cs[0 .. len]; | |
126 } | |
127 } | |
128 | |
129 super(msg); | |
130 } | |
131 } | |
132 | |
133 | |
134 static this() | |
135 { | |
136 version(Win32) | |
137 { | |
138 WSADATA wd; | |
139 | |
140 // Winsock will still load if an older version is present. | |
141 // The version is just a request. | |
142 int val; | |
143 val = WSAStartup(0x2020, &wd); | |
144 if(val) // Request Winsock 2.2 for IPv6. | |
145 throw new SocketException("Unable to initialize socket library", val); | |
146 } | |
147 } | |
148 | |
149 | |
150 static ~this() | |
151 { | |
152 version(Win32) | |
153 { | |
154 WSACleanup(); | |
155 } | |
156 } | |
157 | |
158 /** | |
159 * The communication domain used to resolve an address. | |
160 */ | |
161 enum AddressFamily: int | |
162 { | |
163 UNSPEC = AF_UNSPEC, /// | |
164 UNIX = AF_UNIX, /// local communication | |
165 INET = AF_INET, /// internet protocol version 4 | |
166 IPX = AF_IPX, /// novell IPX | |
167 APPLETALK = AF_APPLETALK, /// appletalk | |
168 INET6 = AF_INET6, // internet protocol version 6 | |
169 } | |
170 | |
171 | |
172 /** | |
173 * Communication semantics | |
174 */ | |
175 enum SocketType: int | |
176 { | |
177 STREAM = SOCK_STREAM, /// sequenced, reliable, two-way communication-based byte streams | |
178 DGRAM = SOCK_DGRAM, /// connectionless, unreliable datagrams with a fixed maximum length; data may be lost or arrive out of order | |
179 RAW = SOCK_RAW, /// raw protocol access | |
180 RDM = SOCK_RDM, /// reliably-delivered message datagrams | |
181 SEQPACKET = SOCK_SEQPACKET, /// sequenced, reliable, two-way connection-based datagrams with a fixed maximum length | |
182 } | |
183 | |
184 | |
185 /** | |
186 * Protocol | |
187 */ | |
188 enum ProtocolType: int | |
189 { | |
190 IP = IPPROTO_IP, /// internet protocol version 4 | |
191 ICMP = IPPROTO_ICMP, /// internet control message protocol | |
192 IGMP = IPPROTO_IGMP, /// internet group management protocol | |
193 GGP = IPPROTO_GGP, /// gateway to gateway protocol | |
194 TCP = IPPROTO_TCP, /// transmission control protocol | |
195 PUP = IPPROTO_PUP, /// PARC universal packet protocol | |
196 UDP = IPPROTO_UDP, /// user datagram protocol | |
197 IDP = IPPROTO_IDP, /// Xerox NS protocol | |
198 IPV6 = IPPROTO_IPV6, /// internet protocol version 6 | |
199 } | |
200 | |
201 | |
202 /** | |
203 * Protocol is a class for retrieving protocol information. | |
204 */ | |
205 class Protocol | |
206 { | |
207 ProtocolType type; /// These members are populated when one of the following functions are called without failure: | |
208 string name; /// ditto | |
209 string[] aliases; /// ditto | |
210 | |
211 | |
212 void populate(protoent* proto) | |
213 { | |
214 type = cast(ProtocolType)proto.p_proto; | |
215 name = std.string.toString(proto.p_name).dup; | |
216 | |
217 int i; | |
218 for(i = 0;; i++) | |
219 { | |
220 if(!proto.p_aliases[i]) | |
221 break; | |
222 } | |
223 | |
224 if(i) | |
225 { | |
226 aliases = new string[i]; | |
227 for(i = 0; i != aliases.length; i++) | |
228 { | |
229 aliases[i] = std.string.toString(proto.p_aliases[i]).dup; | |
230 } | |
231 } | |
232 else | |
233 { | |
234 aliases = null; | |
235 } | |
236 } | |
237 | |
238 /** Returns false on failure */ | |
239 bool getProtocolByName(string name) | |
240 { | |
241 protoent* proto; | |
242 proto = getprotobyname(toStringz(name)); | |
243 if(!proto) | |
244 return false; | |
245 populate(proto); | |
246 return true; | |
247 } | |
248 | |
249 | |
250 /** Returns false on failure */ | |
251 // Same as getprotobynumber(). | |
252 bool getProtocolByType(ProtocolType type) | |
253 { | |
254 version (have_getprotobynumber) | |
255 { | |
256 protoent* proto; | |
257 proto = getprotobynumber(type); | |
258 if(!proto) | |
259 return false; | |
260 populate(proto); | |
261 return true; | |
262 } | |
263 else | |
264 return false; | |
265 } | |
266 } | |
267 | |
268 | |
269 unittest | |
270 { | |
271 Protocol proto = new Protocol; | |
272 assert(proto.getProtocolByType(ProtocolType.TCP)); | |
273 printf("About protocol TCP:\n\tName: %.*s\n", | |
274 cast(int) proto.name.length, proto.name.ptr); | |
275 foreach(string s; proto.aliases) | |
276 { | |
277 printf("\tAlias: %.*s\n", cast(int) s.length, s.ptr); | |
278 } | |
279 } | |
280 | |
281 | |
282 /** | |
283 * Service is a class for retrieving service information. | |
284 */ | |
285 class Service | |
286 { | |
287 /** These members are populated when one of the following functions are called without failure: */ | |
288 string name; | |
289 string[] aliases; /// ditto | |
290 ushort port; /// ditto | |
291 string protocolName; /// ditto | |
292 | |
293 | |
294 void populate(servent* serv) | |
295 { | |
296 name = std.string.toString(serv.s_name).dup; | |
297 port = ntohs(cast(ushort)serv.s_port); | |
298 protocolName = std.string.toString(serv.s_proto).dup; | |
299 | |
300 int i; | |
301 for(i = 0;; i++) | |
302 { | |
303 if(!serv.s_aliases[i]) | |
304 break; | |
305 } | |
306 | |
307 if(i) | |
308 { | |
309 aliases = new string[i]; | |
310 for(i = 0; i != aliases.length; i++) | |
311 { | |
312 aliases[i] = std.string.toString(serv.s_aliases[i]).dup; | |
313 } | |
314 } | |
315 else | |
316 { | |
317 aliases = null; | |
318 } | |
319 } | |
320 | |
321 /** | |
322 * If a protocol name is omitted, any protocol will be matched. | |
323 * Returns: false on failure. | |
324 */ | |
325 bool getServiceByName(string name, string protocolName) | |
326 { | |
327 servent* serv; | |
328 serv = getservbyname(toStringz(name), toStringz(protocolName)); | |
329 if(!serv) | |
330 return false; | |
331 populate(serv); | |
332 return true; | |
333 } | |
334 | |
335 | |
336 // Any protocol name will be matched. | |
337 /// ditto | |
338 bool getServiceByName(string name) | |
339 { | |
340 servent* serv; | |
341 serv = getservbyname(toStringz(name), null); | |
342 if(!serv) | |
343 return false; | |
344 populate(serv); | |
345 return true; | |
346 } | |
347 | |
348 | |
349 /// ditto | |
350 bool getServiceByPort(ushort port, string protocolName) | |
351 { | |
352 version (have_getservbyport) | |
353 { | |
354 servent* serv; | |
355 serv = getservbyport(port, toStringz(protocolName)); | |
356 if(!serv) | |
357 return false; | |
358 populate(serv); | |
359 return true; | |
360 } | |
361 else | |
362 return false; | |
363 } | |
364 | |
365 | |
366 // Any protocol name will be matched. | |
367 /// ditto | |
368 bool getServiceByPort(ushort port) | |
369 { | |
370 version (have_getservbyport) | |
371 { | |
372 servent* serv; | |
373 serv = getservbyport(port, null); | |
374 if(!serv) | |
375 return false; | |
376 populate(serv); | |
377 return true; | |
378 } | |
379 else | |
380 return false; | |
381 } | |
382 } | |
383 | |
384 | |
385 unittest | |
386 { | |
387 Service serv = new Service; | |
388 if(serv.getServiceByName("epmap", "tcp")) | |
389 { | |
390 printf("About service epmap:\n\tService: %.*s\n\tPort: %d\n\tProtocol: %.*s\n", | |
391 cast(int) serv.name.length, serv.name.ptr, serv.port, | |
392 cast(int) serv.protocolName.length, serv.protocolName.ptr); | |
393 foreach(char[] s; serv.aliases) | |
394 { | |
395 printf("\tAlias: %.*s\n", cast(int) s.length, s.ptr); | |
396 } | |
397 } | |
398 else | |
399 { | |
400 printf("No service for epmap.\n"); | |
401 } | |
402 } | |
403 | |
404 | |
405 /** | |
406 * Base exception thrown from an InternetHost. | |
407 */ | |
408 class HostException: Exception | |
409 { | |
410 int errorCode; /// Platform-specific error code. | |
411 | |
412 | |
413 this(string msg, int err = 0) | |
414 { | |
415 errorCode = err; | |
416 super(msg); | |
417 } | |
418 } | |
419 | |
420 /** | |
421 * InternetHost is a class for resolving IPv4 addresses. | |
422 */ | |
423 class InternetHost | |
424 { | |
425 /** These members are populated when one of the following functions are called without failure: */ | |
426 string name; | |
427 string[] aliases; /// ditto | |
428 uint32_t[] addrList; /// ditto | |
429 | |
430 | |
431 void validHostent(hostent* he) | |
432 { | |
433 if(he.h_addrtype != cast(int)AddressFamily.INET || he.h_length != 4) | |
434 throw new HostException("Address family mismatch", _lasterr()); | |
435 } | |
436 | |
437 | |
438 void populate(hostent* he) | |
439 { | |
440 int i; | |
441 char* p; | |
442 | |
443 name = std.string.toString(he.h_name).dup; | |
444 | |
445 for(i = 0;; i++) | |
446 { | |
447 p = he.h_aliases[i]; | |
448 if(!p) | |
449 break; | |
450 } | |
451 | |
452 if(i) | |
453 { | |
454 aliases = new string[i]; | |
455 for(i = 0; i != aliases.length; i++) | |
456 { | |
457 aliases[i] = std.string.toString(he.h_aliases[i]).dup; | |
458 } | |
459 } | |
460 else | |
461 { | |
462 aliases = null; | |
463 } | |
464 | |
465 for(i = 0;; i++) | |
466 { | |
467 p = he.h_addr_list[i]; | |
468 if(!p) | |
469 break; | |
470 } | |
471 | |
472 if(i) | |
473 { | |
474 addrList = new uint32_t[i]; | |
475 for(i = 0; i != addrList.length; i++) | |
476 { | |
477 addrList[i] = ntohl(*(cast(uint32_t*)he.h_addr_list[i])); | |
478 } | |
479 } | |
480 else | |
481 { | |
482 addrList = null; | |
483 } | |
484 } | |
485 | |
486 /** | |
487 * Resolve host name. Returns false if unable to resolve. | |
488 */ | |
489 bool getHostByName(string name) | |
490 { | |
491 hostent* he; | |
492 synchronized(this.classinfo) he = gethostbyname(toStringz(name)); | |
493 if(!he) | |
494 return false; | |
495 validHostent(he); | |
496 populate(he); | |
497 return true; | |
498 } | |
499 | |
500 | |
501 /** | |
502 * Resolve IPv4 address number. Returns false if unable to resolve. | |
503 */ | |
504 bool getHostByAddr(uint addr) | |
505 { | |
506 uint x = htonl(addr); | |
507 hostent* he; | |
508 synchronized(this.classinfo) he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET); | |
509 if(!he) | |
510 return false; | |
511 validHostent(he); | |
512 populate(he); | |
513 return true; | |
514 } | |
515 | |
516 | |
517 /** | |
518 * Same as previous, but addr is an IPv4 address string in the | |
519 * dotted-decimal form $(I a.b.c.d). | |
520 * Returns false if unable to resolve. | |
521 */ | |
522 bool getHostByAddr(string addr) | |
523 { | |
524 uint x = inet_addr(std.string.toStringz(addr)); | |
525 hostent* he; | |
526 synchronized(this.classinfo) he = gethostbyaddr(&x, 4, cast(int)AddressFamily.INET); | |
527 if(!he) | |
528 return false; | |
529 validHostent(he); | |
530 populate(he); | |
531 return true; | |
532 } | |
533 } | |
534 | |
535 | |
536 unittest | |
537 { | |
538 InternetHost ih = new InternetHost; | |
539 assert(ih.getHostByName("www.digitalmars.com")); | |
540 printf("addrList.length = %d\n", ih.addrList.length); | |
541 assert(ih.addrList.length); | |
542 InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY); | |
543 char[] sia = ia.toAddrString(); | |
544 printf("IPaddress = %.*s\nname = %.*s\n", cast(int) sia.length, sia.ptr, | |
545 cast(int) ih.name.length, ih.name.ptr); | |
546 foreach(int i, string s; ih.aliases) | |
547 { | |
548 printf("aliases[%d] = %.*s\n", i, cast(int) s.length, s.ptr); | |
549 } | |
550 | |
551 printf("---\n"); | |
552 | |
553 assert(ih.getHostByAddr(ih.addrList[0])); | |
554 printf("name = %.*s\n", cast(int) ih.name.length, ih.name.ptr); | |
555 foreach(int i, string s; ih.aliases) | |
556 { | |
557 printf("aliases[%d] = %.*s\n", i, cast(int) s.length, s.ptr); | |
558 } | |
559 } | |
560 | |
561 | |
562 /** | |
563 * Base exception thrown from an Address. | |
564 */ | |
565 class AddressException: Exception | |
566 { | |
567 this(string msg) | |
568 { | |
569 super(msg); | |
570 } | |
571 } | |
572 | |
573 | |
574 /** | |
575 * Address is an abstract class for representing a network addresses. | |
576 */ | |
577 abstract class Address | |
578 { | |
579 protected sockaddr* name(); | |
580 protected int nameLen(); | |
581 AddressFamily addressFamily(); /// Family of this address. | |
582 string toString(); /// Human readable string representing this address. | |
583 } | |
584 | |
585 /** | |
586 * | |
587 */ | |
588 class UnknownAddress: Address | |
589 { | |
590 protected: | |
591 sockaddr sa; | |
592 | |
593 | |
594 sockaddr* name() | |
595 { | |
596 return &sa; | |
597 } | |
598 | |
599 | |
600 int nameLen() | |
601 { | |
602 return sa.sizeof; | |
603 } | |
604 | |
605 | |
606 public: | |
607 AddressFamily addressFamily() | |
608 { | |
609 return cast(AddressFamily)sa.sa_family; | |
610 } | |
611 | |
612 | |
613 string toString() | |
614 { | |
615 return "Unknown"; | |
616 } | |
617 } | |
618 | |
619 | |
620 /** | |
621 * InternetAddress is a class that represents an IPv4 (internet protocol version | |
622 * 4) address and port. | |
623 */ | |
624 class InternetAddress: Address | |
625 { | |
626 protected: | |
627 sockaddr_in sin; | |
628 | |
629 | |
630 sockaddr* name() | |
631 { | |
632 return cast(sockaddr*)&sin; | |
633 } | |
634 | |
635 | |
636 int nameLen() | |
637 { | |
638 return sin.sizeof; | |
639 } | |
640 | |
641 | |
642 this() | |
643 { | |
644 } | |
645 | |
646 | |
647 public: | |
648 const uint ADDR_ANY = INADDR_ANY; /// Any IPv4 address number. | |
649 const uint ADDR_NONE = INADDR_NONE; /// An invalid IPv4 address number. | |
650 const ushort PORT_ANY = 0; /// Any IPv4 port number. | |
651 | |
652 /// Overridden to return AddressFamily.INET. | |
653 AddressFamily addressFamily() | |
654 { | |
655 return cast(AddressFamily)AddressFamily.INET; | |
656 } | |
657 | |
658 /// Returns the IPv4 port number. | |
659 ushort port() | |
660 { | |
661 return ntohs(sin.sin_port); | |
662 } | |
663 | |
664 /// Returns the IPv4 address number. | |
665 uint addr() | |
666 { | |
667 return ntohl(sin.sin_addr.s_addr); | |
668 } | |
669 | |
670 /** | |
671 * Params: | |
672 * addr = an IPv4 address string in the dotted-decimal form a.b.c.d, | |
673 * or a host name that will be resolved using an InternetHost | |
674 * object. | |
675 * port = may be PORT_ANY as stated below. | |
676 */ | |
677 this(string addr, ushort port) | |
678 { | |
679 uint uiaddr = parse(addr); | |
680 if(ADDR_NONE == uiaddr) | |
681 { | |
682 InternetHost ih = new InternetHost; | |
683 if(!ih.getHostByName(addr)) | |
684 //throw new AddressException("Invalid internet address"); | |
685 throw new AddressException("Unable to resolve host '" ~ addr ~ "'"); | |
686 uiaddr = ih.addrList[0]; | |
687 } | |
688 sin.sin_addr.s_addr = htonl(uiaddr); | |
689 sin.sin_port = htons(port); | |
690 } | |
691 | |
692 /** | |
693 * Construct a new Address. addr may be ADDR_ANY (default) and port may | |
694 * be PORT_ANY, and the actual numbers may not be known until a connection | |
695 * is made. | |
696 */ | |
697 this(uint addr, ushort port) | |
698 { | |
699 sin.sin_addr.s_addr = htonl(addr); | |
700 sin.sin_port = htons(port); | |
701 } | |
702 | |
703 /// ditto | |
704 this(ushort port) | |
705 { | |
706 sin.sin_addr.s_addr = 0; //any, "0.0.0.0" | |
707 sin.sin_port = htons(port); | |
708 } | |
709 | |
710 /// Human readable string representing the IPv4 address in dotted-decimal form. | |
711 string toAddrString() | |
712 { | |
713 return std.string.toString(inet_ntoa(sin.sin_addr)).dup; | |
714 } | |
715 | |
716 /// Human readable string representing the IPv4 port. | |
717 string toPortString() | |
718 { | |
719 return std.string.toString(port()); | |
720 } | |
721 | |
722 /// Human readable string representing the IPv4 address and port in the form $(I a.b.c.d:e). | |
723 string toString() | |
724 { | |
725 return toAddrString() ~ ":" ~ toPortString(); | |
726 } | |
727 | |
728 /** | |
729 * Parse an IPv4 address string in the dotted-decimal form $(I a.b.c.d) | |
730 * and return the number. | |
731 * If the string is not a legitimate IPv4 address, | |
732 * ADDR_NONE is returned. | |
733 */ | |
734 static uint parse(string addr) | |
735 { | |
736 return ntohl(inet_addr(std.string.toStringz(addr))); | |
737 } | |
738 } | |
739 | |
740 | |
741 unittest | |
742 { | |
743 InternetAddress ia = new InternetAddress("63.105.9.61", 80); | |
744 assert(ia.toString() == "63.105.9.61:80"); | |
745 } | |
746 | |
747 | |
748 /** */ | |
749 class SocketAcceptException: SocketException | |
750 { | |
751 this(string msg, int err = 0) | |
752 { | |
753 super(msg, err); | |
754 } | |
755 } | |
756 | |
757 /// How a socket is shutdown: | |
758 enum SocketShutdown: int | |
759 { | |
760 RECEIVE = SD_RECEIVE, /// socket receives are disallowed | |
761 SEND = SD_SEND, /// socket sends are disallowed | |
762 BOTH = SD_BOTH, /// both RECEIVE and SEND | |
763 } | |
764 | |
765 | |
766 /// Flags may be OR'ed together: | |
767 enum SocketFlags: int | |
768 { | |
769 NONE = 0, /// no flags specified | |
770 | |
771 OOB = MSG_OOB, /// out-of-band stream data | |
772 PEEK = MSG_PEEK, /// peek at incoming data without removing it from the queue, only for receiving | |
773 DONTROUTE = MSG_DONTROUTE, /// data should not be subject to routing; this flag may be ignored. Only for sending | |
774 NOSIGNAL = MSG_NOSIGNAL, /// don't send SIGPIPE signal on socket write error and instead return EPIPE | |
775 } | |
776 | |
777 | |
778 /// Duration timeout value. | |
779 extern(C) struct timeval | |
780 { | |
781 // D interface | |
782 int seconds; /// Number of seconds. | |
783 int microseconds; /// Number of additional microseconds. | |
784 | |
785 // C interface | |
786 deprecated | |
787 { | |
788 alias seconds tv_sec; | |
789 alias microseconds tv_usec; | |
790 } | |
791 } | |
792 | |
793 | |
794 /// A collection of sockets for use with Socket.select. | |
795 class SocketSet | |
796 { | |
797 private: | |
798 uint maxsockets; /// max desired sockets, the fd_set might be capable of holding more | |
799 fd_set set; | |
800 | |
801 | |
802 version(Win32) | |
803 { | |
804 uint count() | |
805 { | |
806 return set.fd_count; | |
807 } | |
808 } | |
809 else version(BsdSockets) | |
810 { | |
811 int maxfd; | |
812 uint count; | |
813 } | |
814 | |
815 | |
816 public: | |
817 | |
818 /// Set the maximum amount of sockets that may be added. | |
819 this(uint max) | |
820 { | |
821 maxsockets = max; | |
822 reset(); | |
823 } | |
824 | |
825 /// Uses the default maximum for the system. | |
826 this() | |
827 { | |
828 this(FD_SETSIZE); | |
829 } | |
830 | |
831 /// Reset the SocketSet so that there are 0 Sockets in the collection. | |
832 void reset() | |
833 { | |
834 FD_ZERO(&set); | |
835 | |
836 version(BsdSockets) | |
837 { | |
838 maxfd = -1; | |
839 count = 0; | |
840 } | |
841 } | |
842 | |
843 | |
844 void add(socket_t s) | |
845 in | |
846 { | |
847 // Make sure too many sockets don't get added. | |
848 assert(count < maxsockets); | |
849 version(BsdSockets) | |
850 { | |
851 version(GNU) | |
852 { | |
853 // Tries to account for little and big endian..er needs work | |
854 // assert((s/NFDBITS+1)*NFDBITS/8 <= nbytes); | |
855 } | |
856 else | |
857 { | |
858 assert(FDELT(s) < (FD_SETSIZE / NFDBITS)); | |
859 } | |
860 } | |
861 } | |
862 body | |
863 { | |
864 FD_SET(s, &set); | |
865 | |
866 version(BsdSockets) | |
867 { | |
868 ++count; | |
869 if(s > maxfd) | |
870 maxfd = s; | |
871 } | |
872 } | |
873 | |
874 /// Add a Socket to the collection. Adding more than the maximum has dangerous side affects. | |
875 void add(Socket s) | |
876 { | |
877 add(s.sock); | |
878 } | |
879 | |
880 void remove(socket_t s) | |
881 { | |
882 FD_CLR(s, &set); | |
883 version(BsdSockets) | |
884 { | |
885 --count; | |
886 // note: adjusting maxfd would require scanning the set, not worth it | |
887 } | |
888 } | |
889 | |
890 | |
891 /// Remove this Socket from the collection. | |
892 void remove(Socket s) | |
893 { | |
894 remove(s.sock); | |
895 } | |
896 | |
897 int isSet(socket_t s) | |
898 { | |
899 return FD_ISSET(s, &set); | |
900 } | |
901 | |
902 | |
903 /// Returns nonzero if this Socket is in the collection. | |
904 int isSet(Socket s) | |
905 { | |
906 return isSet(s.sock); | |
907 } | |
908 | |
909 | |
910 /// Return maximum amount of sockets that can be added, like FD_SETSIZE. | |
911 uint max() | |
912 { | |
913 return maxsockets; | |
914 } | |
915 | |
916 | |
917 fd_set* toFd_set() | |
918 { | |
919 return &set; | |
920 } | |
921 | |
922 | |
923 int selectn() | |
924 { | |
925 version(Win32) | |
926 { | |
927 return count; | |
928 } | |
929 else version(BsdSockets) | |
930 { | |
931 return maxfd + 1; | |
932 } | |
933 } | |
934 } | |
935 | |
936 | |
937 /// The level at which a socket option is defined: | |
938 enum SocketOptionLevel: int | |
939 { | |
940 SOCKET = SOL_SOCKET, /// socket level | |
941 IP = ProtocolType.IP, /// internet protocol version 4 level | |
942 ICMP = ProtocolType.ICMP, /// | |
943 IGMP = ProtocolType.IGMP, /// | |
944 GGP = ProtocolType.GGP, /// | |
945 TCP = ProtocolType.TCP, /// transmission control protocol level | |
946 PUP = ProtocolType.PUP, /// | |
947 UDP = ProtocolType.UDP, /// user datagram protocol level | |
948 IDP = ProtocolType.IDP, /// | |
949 IPV6 = ProtocolType.IPV6, /// internet protocol version 6 level | |
950 } | |
951 | |
952 | |
953 /// Linger information for use with SocketOption.LINGER. | |
954 extern(C) struct linger | |
955 { | |
956 version (BsdSockets) | |
957 version (GNU) | |
958 { | |
959 private alias std.c.unix.unix.linger __unix_linger; | |
960 static assert(linger.sizeof == __unix_linger.sizeof); | |
961 } | |
962 // D interface | |
963 version(Win32) | |
964 { | |
965 uint16_t on; /// Nonzero for on. | |
966 uint16_t time; /// Linger time. | |
967 } | |
968 else version(BsdSockets) | |
969 { | |
970 version (GNU) | |
971 { | |
972 | |
973 typeof(__unix_linger.l_onoff) on; | |
974 typeof(__unix_linger.l_linger) time; | |
975 | |
976 } | |
977 else | |
978 { | |
979 int32_t on; | |
980 int32_t time; | |
981 } | |
982 } | |
983 | |
984 // C interface | |
985 deprecated | |
986 { | |
987 alias on l_onoff; | |
988 alias time l_linger; | |
989 } | |
990 } | |
991 | |
992 | |
993 /// Specifies a socket option: | |
994 enum SocketOption: int | |
995 { | |
996 DEBUG = SO_DEBUG, /// record debugging information | |
997 BROADCAST = SO_BROADCAST, /// allow transmission of broadcast messages | |
998 REUSEADDR = SO_REUSEADDR, /// allow local reuse of address | |
999 LINGER = SO_LINGER, /// linger on close if unsent data is present | |
1000 OOBINLINE = SO_OOBINLINE, /// receive out-of-band data in band | |
1001 SNDBUF = SO_SNDBUF, /// send buffer size | |
1002 RCVBUF = SO_RCVBUF, /// receive buffer size | |
1003 DONTROUTE = SO_DONTROUTE, /// do not route | |
1004 | |
1005 // SocketOptionLevel.TCP: | |
1006 TCP_NODELAY = .TCP_NODELAY, /// disable the Nagle algorithm for send coalescing | |
1007 | |
1008 // SocketOptionLevel.IPV6: | |
1009 IPV6_UNICAST_HOPS = .IPV6_UNICAST_HOPS, /// | |
1010 IPV6_MULTICAST_IF = .IPV6_MULTICAST_IF, /// | |
1011 IPV6_MULTICAST_LOOP = .IPV6_MULTICAST_LOOP, /// | |
1012 IPV6_JOIN_GROUP = .IPV6_JOIN_GROUP, /// | |
1013 IPV6_LEAVE_GROUP = .IPV6_LEAVE_GROUP, /// | |
1014 } | |
1015 | |
1016 | |
1017 /** | |
1018 * Socket is a class that creates a network communication endpoint using the | |
1019 * Berkeley sockets interface. | |
1020 */ | |
1021 class Socket | |
1022 { | |
1023 private: | |
1024 socket_t sock; | |
1025 AddressFamily _family; | |
1026 | |
1027 version(Win32) | |
1028 bool _blocking = false; /// Property to get or set whether the socket is blocking or nonblocking. | |
1029 | |
1030 | |
1031 // For use with accepting(). | |
1032 protected this() | |
1033 { | |
1034 } | |
1035 | |
1036 | |
1037 public: | |
1038 | |
1039 /** | |
1040 * Create a blocking socket. If a single protocol type exists to support | |
1041 * this socket type within the address family, the ProtocolType may be | |
1042 * omitted. | |
1043 */ | |
1044 this(AddressFamily af, SocketType type, ProtocolType protocol) | |
1045 { | |
1046 sock = cast(socket_t)socket(af, type, protocol); | |
1047 if(sock == socket_t.init) | |
1048 throw new SocketException("Unable to create socket", _lasterr()); | |
1049 _family = af; | |
1050 } | |
1051 | |
1052 | |
1053 // A single protocol exists to support this socket type within the | |
1054 // protocol family, so the ProtocolType is assumed. | |
1055 /// ditto | |
1056 this(AddressFamily af, SocketType type) | |
1057 { | |
1058 this(af, type, cast(ProtocolType)0); // Pseudo protocol number. | |
1059 } | |
1060 | |
1061 | |
1062 /// ditto | |
1063 this(AddressFamily af, SocketType type, string protocolName) | |
1064 { | |
1065 protoent* proto; | |
1066 proto = getprotobyname(toStringz(protocolName)); | |
1067 if(!proto) | |
1068 throw new SocketException("Unable to find the protocol", _lasterr()); | |
1069 this(af, type, cast(ProtocolType)proto.p_proto); | |
1070 } | |
1071 | |
1072 | |
1073 ~this() | |
1074 { | |
1075 close(); | |
1076 } | |
1077 | |
1078 | |
1079 /// Get underlying socket handle. | |
1080 socket_t handle() | |
1081 { | |
1082 return sock; | |
1083 } | |
1084 | |
1085 /** | |
1086 * Get/set socket's blocking flag. | |
1087 * | |
1088 * When a socket is blocking, calls to receive(), accept(), and send() | |
1089 * will block and wait for data/action. | |
1090 * A non-blocking socket will immediately return instead of blocking. | |
1091 */ | |
1092 bool blocking() | |
1093 { | |
1094 version(Win32) | |
1095 { | |
1096 return _blocking; | |
1097 } | |
1098 else version(BsdSockets) | |
1099 { | |
1100 return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK); | |
1101 } | |
1102 } | |
1103 | |
1104 /// ditto | |
1105 void blocking(bool byes) | |
1106 { | |
1107 version(Win32) | |
1108 { | |
1109 uint num = !byes; | |
1110 if(_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num)) | |
1111 goto err; | |
1112 _blocking = byes; | |
1113 } | |
1114 else version(BsdSockets) | |
1115 { | |
1116 int x = fcntl(sock, F_GETFL, 0); | |
1117 if(-1 == x) | |
1118 goto err; | |
1119 if(byes) | |
1120 x &= ~O_NONBLOCK; | |
1121 else | |
1122 x |= O_NONBLOCK; | |
1123 if(-1 == fcntl(sock, F_SETFL, x)) | |
1124 goto err; | |
1125 } | |
1126 return; // Success. | |
1127 | |
1128 err: | |
1129 throw new SocketException("Unable to set socket blocking", _lasterr()); | |
1130 } | |
1131 | |
1132 | |
1133 /// Get the socket's address family. | |
1134 AddressFamily addressFamily() // getter | |
1135 { | |
1136 return _family; | |
1137 } | |
1138 | |
1139 /// Property that indicates if this is a valid, alive socket. | |
1140 bool isAlive() // getter | |
1141 { | |
1142 int type, typesize = type.sizeof; | |
1143 return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize); | |
1144 } | |
1145 | |
1146 /// Associate a local address with this socket. | |
1147 void bind(Address addr) | |
1148 { | |
1149 if(_SOCKET_ERROR == .bind(sock, addr.name(), addr.nameLen())) | |
1150 throw new SocketException("Unable to bind socket", _lasterr()); | |
1151 } | |
1152 | |
1153 /** | |
1154 * Establish a connection. If the socket is blocking, connect waits for | |
1155 * the connection to be made. If the socket is nonblocking, connect | |
1156 * returns immediately and the connection attempt is still in progress. | |
1157 */ | |
1158 void connect(Address to) | |
1159 { | |
1160 if(_SOCKET_ERROR == .connect(sock, to.name(), to.nameLen())) | |
1161 { | |
1162 int err; | |
1163 err = _lasterr(); | |
1164 | |
1165 if(!blocking) | |
1166 { | |
1167 version(Win32) | |
1168 { | |
1169 if(WSAEWOULDBLOCK == err) | |
1170 return; | |
1171 } | |
1172 else version(Unix) | |
1173 { | |
1174 if(EINPROGRESS == err) | |
1175 return; | |
1176 } | |
1177 else | |
1178 { | |
1179 static assert(0); | |
1180 } | |
1181 } | |
1182 throw new SocketException("Unable to connect socket", err); | |
1183 } | |
1184 } | |
1185 | |
1186 /** | |
1187 * Listen for an incoming connection. bind must be called before you can | |
1188 * listen. The backlog is a request of how many pending incoming | |
1189 * connections are queued until accept'ed. | |
1190 */ | |
1191 void listen(int backlog) | |
1192 { | |
1193 if(_SOCKET_ERROR == .listen(sock, backlog)) | |
1194 throw new SocketException("Unable to listen on socket", _lasterr()); | |
1195 } | |
1196 | |
1197 /** | |
1198 * Called by accept when a new Socket must be created for a new | |
1199 * connection. To use a derived class, override this method and return an | |
1200 * instance of your class. The returned Socket's handle must not be set; | |
1201 * Socket has a protected constructor this() to use in this situation. | |
1202 */ | |
1203 // Override to use a derived class. | |
1204 // The returned socket's handle must not be set. | |
1205 protected Socket accepting() | |
1206 { | |
1207 return new Socket; | |
1208 } | |
1209 | |
1210 /** | |
1211 * Accept an incoming connection. If the socket is blocking, accept | |
1212 * waits for a connection request. Throws SocketAcceptException if unable | |
1213 * to accept. See accepting for use with derived classes. | |
1214 */ | |
1215 Socket accept() | |
1216 { | |
1217 socket_t newsock; | |
1218 //newsock = cast(socket_t).accept(sock, null, null); // DMD 0.101 error: found '(' when expecting ';' following 'statement | |
1219 alias .accept topaccept; | |
1220 newsock = cast(socket_t)topaccept(sock, null, null); | |
1221 if(socket_t.init == newsock) | |
1222 throw new SocketAcceptException("Unable to accept socket connection", _lasterr()); | |
1223 | |
1224 Socket newSocket; | |
1225 try | |
1226 { | |
1227 newSocket = accepting(); | |
1228 assert(newSocket.sock == socket_t.init); | |
1229 | |
1230 newSocket.sock = newsock; | |
1231 version(Win32) | |
1232 newSocket._blocking = _blocking; //inherits blocking mode | |
1233 newSocket._family = _family; //same family | |
1234 } | |
1235 catch(Object o) | |
1236 { | |
1237 _close(newsock); | |
1238 throw o; | |
1239 } | |
1240 | |
1241 return newSocket; | |
1242 } | |
1243 | |
1244 /// Disables sends and/or receives. | |
1245 void shutdown(SocketShutdown how) | |
1246 { | |
1247 .shutdown(sock, cast(int)how); | |
1248 } | |
1249 | |
1250 | |
1251 private static void _close(socket_t sock) | |
1252 { | |
1253 version(Win32) | |
1254 { | |
1255 .closesocket(sock); | |
1256 } | |
1257 else version(BsdSockets) | |
1258 { | |
1259 .close(sock); | |
1260 } | |
1261 } | |
1262 | |
1263 | |
1264 /** | |
1265 * Immediately drop any connections and release socket resources. | |
1266 * Calling shutdown before close is recommended for connection-oriented | |
1267 * sockets. The Socket object is no longer usable after close. | |
1268 */ | |
1269 //calling shutdown() before this is recommended | |
1270 //for connection-oriented sockets | |
1271 void close() | |
1272 { | |
1273 _close(sock); | |
1274 sock = socket_t.init; | |
1275 } | |
1276 | |
1277 | |
1278 private Address newFamilyObject() | |
1279 { | |
1280 Address result; | |
1281 switch(_family) | |
1282 { | |
1283 case cast(AddressFamily)AddressFamily.INET: | |
1284 result = new InternetAddress; | |
1285 break; | |
1286 | |
1287 default: | |
1288 result = new UnknownAddress; | |
1289 } | |
1290 return result; | |
1291 } | |
1292 | |
1293 | |
1294 /// Returns the local machine's host name. Idea from mango. | |
1295 static string hostName() // getter | |
1296 { | |
1297 char[256] result; // Host names are limited to 255 chars. | |
1298 if(_SOCKET_ERROR == .gethostname(result.ptr, result.length)) | |
1299 throw new SocketException("Unable to obtain host name", _lasterr()); | |
1300 return std.string.toString(cast(char*)result).dup; | |
1301 } | |
1302 | |
1303 /// Remote endpoint Address. | |
1304 Address remoteAddress() | |
1305 { | |
1306 Address addr = newFamilyObject(); | |
1307 int nameLen = addr.nameLen(); | |
1308 if(_SOCKET_ERROR == .getpeername(sock, addr.name(), &nameLen)) | |
1309 throw new SocketException("Unable to obtain remote socket address", _lasterr()); | |
1310 assert(addr.addressFamily() == _family); | |
1311 return addr; | |
1312 } | |
1313 | |
1314 /// Local endpoint Address. | |
1315 Address localAddress() | |
1316 { | |
1317 Address addr = newFamilyObject(); | |
1318 int nameLen = addr.nameLen(); | |
1319 if(_SOCKET_ERROR == .getsockname(sock, addr.name(), &nameLen)) | |
1320 throw new SocketException("Unable to obtain local socket address", _lasterr()); | |
1321 assert(addr.addressFamily() == _family); | |
1322 return addr; | |
1323 } | |
1324 | |
1325 /// Send or receive error code. | |
1326 const int ERROR = _SOCKET_ERROR; | |
1327 | |
1328 /** | |
1329 * Send data on the connection. Returns the number of bytes actually | |
1330 * sent, or ERROR on failure. If the socket is blocking and there is no | |
1331 * buffer space left, send waits. | |
1332 */ | |
1333 //returns number of bytes actually sent, or -1 on error | |
1334 int send(void[] buf, SocketFlags flags) | |
1335 { | |
1336 flags |= SocketFlags.NOSIGNAL; | |
1337 int sent = .send(sock, buf.ptr, buf.length, cast(int)flags); | |
1338 return sent; | |
1339 } | |
1340 | |
1341 /// ditto | |
1342 int send(void[] buf) | |
1343 { | |
1344 return send(buf, SocketFlags.NOSIGNAL); | |
1345 } | |
1346 | |
1347 /** | |
1348 * Send data to a specific destination Address. If the destination address is not specified, a connection must have been made and that address is used. If the socket is blocking and there is no buffer space left, sendTo waits. | |
1349 */ | |
1350 int sendTo(void[] buf, SocketFlags flags, Address to) | |
1351 { | |
1352 flags |= SocketFlags.NOSIGNAL; | |
1353 int sent = .sendto(sock, buf.ptr, buf.length, cast(int)flags, to.name(), to.nameLen()); | |
1354 return sent; | |
1355 } | |
1356 | |
1357 /// ditto | |
1358 int sendTo(void[] buf, Address to) | |
1359 { | |
1360 return sendTo(buf, SocketFlags.NONE, to); | |
1361 } | |
1362 | |
1363 | |
1364 //assumes you connect()ed | |
1365 /// ditto | |
1366 int sendTo(void[] buf, SocketFlags flags) | |
1367 { | |
1368 flags |= SocketFlags.NOSIGNAL; | |
1369 int sent = .sendto(sock, buf.ptr, buf.length, cast(int)flags, null, 0); | |
1370 return sent; | |
1371 } | |
1372 | |
1373 | |
1374 //assumes you connect()ed | |
1375 /// ditto | |
1376 int sendTo(void[] buf) | |
1377 { | |
1378 return sendTo(buf, SocketFlags.NONE); | |
1379 } | |
1380 | |
1381 | |
1382 /** | |
1383 * Receive data on the connection. Returns the number of bytes actually | |
1384 * received, 0 if the remote side has closed the connection, or ERROR on | |
1385 * failure. If the socket is blocking, receive waits until there is data | |
1386 * to be received. | |
1387 */ | |
1388 //returns number of bytes actually received, 0 on connection closure, or -1 on error | |
1389 int receive(void[] buf, SocketFlags flags) | |
1390 { | |
1391 if(!buf.length) //return 0 and don't think the connection closed | |
1392 return 0; | |
1393 int read = .recv(sock, buf.ptr, buf.length, cast(int)flags); | |
1394 // if(!read) //connection closed | |
1395 return read; | |
1396 } | |
1397 | |
1398 /// ditto | |
1399 int receive(void[] buf) | |
1400 { | |
1401 return receive(buf, SocketFlags.NONE); | |
1402 } | |
1403 | |
1404 /** | |
1405 * Receive data and get the remote endpoint Address. | |
1406 * If the socket is blocking, receiveFrom waits until there is data to | |
1407 * be received. | |
1408 * Returns: the number of bytes actually received, | |
1409 * 0 if the remote side has closed the connection, or ERROR on failure. | |
1410 */ | |
1411 int receiveFrom(void[] buf, SocketFlags flags, out Address from) | |
1412 { | |
1413 if(!buf.length) //return 0 and don't think the connection closed | |
1414 return 0; | |
1415 from = newFamilyObject(); | |
1416 int nameLen = from.nameLen(); | |
1417 int read = .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, from.name(), &nameLen); | |
1418 assert(from.addressFamily() == _family); | |
1419 // if(!read) //connection closed | |
1420 return read; | |
1421 } | |
1422 | |
1423 | |
1424 /// ditto | |
1425 int receiveFrom(void[] buf, out Address from) | |
1426 { | |
1427 return receiveFrom(buf, SocketFlags.NONE, from); | |
1428 } | |
1429 | |
1430 | |
1431 //assumes you connect()ed | |
1432 /// ditto | |
1433 int receiveFrom(void[] buf, SocketFlags flags) | |
1434 { | |
1435 if(!buf.length) //return 0 and don't think the connection closed | |
1436 return 0; | |
1437 int read = .recvfrom(sock, buf.ptr, buf.length, cast(int)flags, null, null); | |
1438 // if(!read) //connection closed | |
1439 return read; | |
1440 } | |
1441 | |
1442 | |
1443 //assumes you connect()ed | |
1444 /// ditto | |
1445 int receiveFrom(void[] buf) | |
1446 { | |
1447 return receiveFrom(buf, SocketFlags.NONE); | |
1448 } | |
1449 | |
1450 | |
1451 /// Get a socket option. Returns the number of bytes written to result. | |
1452 //returns the length, in bytes, of the actual result - very different from getsockopt() | |
1453 int getOption(SocketOptionLevel level, SocketOption option, void[] result) | |
1454 { | |
1455 int len = result.length; | |
1456 if(_SOCKET_ERROR == .getsockopt(sock, cast(int)level, cast(int)option, result.ptr, &len)) | |
1457 throw new SocketException("Unable to get socket option", _lasterr()); | |
1458 return len; | |
1459 } | |
1460 | |
1461 | |
1462 /// Common case of getting integer and boolean options. | |
1463 int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) | |
1464 { | |
1465 return getOption(level, option, (&result)[0 .. 1]); | |
1466 } | |
1467 | |
1468 | |
1469 /// Get the linger option. | |
1470 int getOption(SocketOptionLevel level, SocketOption option, out linger result) | |
1471 { | |
1472 //return getOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&result)[0 .. 1]); | |
1473 return getOption(level, option, (&result)[0 .. 1]); | |
1474 } | |
1475 | |
1476 // Set a socket option. | |
1477 void setOption(SocketOptionLevel level, SocketOption option, void[] value) | |
1478 { | |
1479 if(_SOCKET_ERROR == .setsockopt(sock, cast(int)level, cast(int)option, value.ptr, value.length)) | |
1480 throw new SocketException("Unable to set socket option", _lasterr()); | |
1481 } | |
1482 | |
1483 | |
1484 /// Common case for setting integer and boolean options. | |
1485 void setOption(SocketOptionLevel level, SocketOption option, int32_t value) | |
1486 { | |
1487 setOption(level, option, (&value)[0 .. 1]); | |
1488 } | |
1489 | |
1490 | |
1491 /// Set the linger option. | |
1492 void setOption(SocketOptionLevel level, SocketOption option, linger value) | |
1493 { | |
1494 //setOption(cast(SocketOptionLevel)SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]); | |
1495 setOption(level, option, (&value)[0 .. 1]); | |
1496 } | |
1497 | |
1498 | |
1499 /** | |
1500 * Wait for a socket to change status. A wait timeout timeval or int microseconds may be specified; if a timeout is not specified or the timeval is null, the maximum timeout is used. The timeval timeout has an unspecified value when select returns. Returns the number of sockets with status changes, 0 on timeout, or -1 on interruption. If the return value is greater than 0, the SocketSets are updated to only contain the sockets having status changes. For a connecting socket, a write status change means the connection is established and it's able to send. For a listening socket, a read status change means there is an incoming connection request and it's able to accept. | |
1501 */ | |
1502 //SocketSet's updated to include only those sockets which an event occured | |
1503 //returns the number of events, 0 on timeout, or -1 on interruption | |
1504 //for a connect()ing socket, writeability means connected | |
1505 //for a listen()ing socket, readability means listening | |
1506 //Winsock: possibly internally limited to 64 sockets per set | |
1507 static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, timeval* tv) | |
1508 in | |
1509 { | |
1510 //make sure none of the SocketSet's are the same object | |
1511 if(checkRead) | |
1512 { | |
1513 assert(checkRead !is checkWrite); | |
1514 assert(checkRead !is checkError); | |
1515 } | |
1516 if(checkWrite) | |
1517 { | |
1518 assert(checkWrite !is checkError); | |
1519 } | |
1520 } | |
1521 body | |
1522 { | |
1523 fd_set* fr, fw, fe; | |
1524 int n = 0; | |
1525 | |
1526 version(Win32) | |
1527 { | |
1528 // Windows has a problem with empty fd_set`s that aren't null. | |
1529 fr = (checkRead && checkRead.count()) ? checkRead.toFd_set() : null; | |
1530 fw = (checkWrite && checkWrite.count()) ? checkWrite.toFd_set() : null; | |
1531 fe = (checkError && checkError.count()) ? checkError.toFd_set() : null; | |
1532 } | |
1533 else | |
1534 { | |
1535 if(checkRead) | |
1536 { | |
1537 fr = checkRead.toFd_set(); | |
1538 n = checkRead.selectn(); | |
1539 } | |
1540 else | |
1541 { | |
1542 fr = null; | |
1543 } | |
1544 | |
1545 if(checkWrite) | |
1546 { | |
1547 fw = checkWrite.toFd_set(); | |
1548 int _n; | |
1549 _n = checkWrite.selectn(); | |
1550 if(_n > n) | |
1551 n = _n; | |
1552 } | |
1553 else | |
1554 { | |
1555 fw = null; | |
1556 } | |
1557 | |
1558 if(checkError) | |
1559 { | |
1560 fe = checkError.toFd_set(); | |
1561 int _n; | |
1562 _n = checkError.selectn(); | |
1563 if(_n > n) | |
1564 n = _n; | |
1565 } | |
1566 else | |
1567 { | |
1568 fe = null; | |
1569 } | |
1570 } | |
1571 | |
1572 int result = .select(n, fr, fw, fe, cast(_ctimeval*)tv); | |
1573 | |
1574 version(Win32) | |
1575 { | |
1576 if(_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR) | |
1577 return -1; | |
1578 } | |
1579 else version(Unix) | |
1580 { | |
1581 if(_SOCKET_ERROR == result && getErrno() == EINTR) | |
1582 return -1; | |
1583 } | |
1584 else | |
1585 { | |
1586 static assert(0); | |
1587 } | |
1588 | |
1589 if(_SOCKET_ERROR == result) | |
1590 throw new SocketException("Socket select error", _lasterr()); | |
1591 | |
1592 return result; | |
1593 } | |
1594 | |
1595 | |
1596 /// ditto | |
1597 static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, int microseconds) | |
1598 { | |
1599 timeval tv; | |
1600 tv.seconds = 0; | |
1601 tv.microseconds = microseconds; | |
1602 return select(checkRead, checkWrite, checkError, &tv); | |
1603 } | |
1604 | |
1605 | |
1606 /// ditto | |
1607 //maximum timeout | |
1608 static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError) | |
1609 { | |
1610 return select(checkRead, checkWrite, checkError, null); | |
1611 } | |
1612 | |
1613 | |
1614 /+ | |
1615 bool poll(events) | |
1616 { | |
1617 int WSAEventSelect(socket_t s, WSAEVENT hEventObject, int lNetworkEvents); // Winsock 2 ? | |
1618 int poll(pollfd* fds, int nfds, int timeout); // Unix ? | |
1619 } | |
1620 +/ | |
1621 } | |
1622 | |
1623 | |
1624 /// TcpSocket is a shortcut class for a TCP Socket. | |
1625 class TcpSocket: Socket | |
1626 { | |
1627 /// Constructs a blocking TCP Socket. | |
1628 this(AddressFamily family) | |
1629 { | |
1630 super(family, SocketType.STREAM, ProtocolType.TCP); | |
1631 } | |
1632 | |
1633 /// Constructs a blocking TCP Socket. | |
1634 this() | |
1635 { | |
1636 this(cast(AddressFamily)AddressFamily.INET); | |
1637 } | |
1638 | |
1639 | |
1640 //shortcut | |
1641 /// Constructs a blocking TCP Socket and connects to an InternetAddress. | |
1642 this(Address connectTo) | |
1643 { | |
1644 this(connectTo.addressFamily()); | |
1645 connect(connectTo); | |
1646 } | |
1647 } | |
1648 | |
1649 | |
1650 /// UdpSocket is a shortcut class for a UDP Socket. | |
1651 class UdpSocket: Socket | |
1652 { | |
1653 /// Constructs a blocking UDP Socket. | |
1654 this(AddressFamily family) | |
1655 { | |
1656 super(family, SocketType.DGRAM, ProtocolType.UDP); | |
1657 } | |
1658 | |
1659 | |
1660 /// Constructs a blocking UDP Socket. | |
1661 this() | |
1662 { | |
1663 this(cast(AddressFamily)AddressFamily.INET); | |
1664 } | |
1665 } | |
1666 |