1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 /// 6 module dfl.socket; 7 8 9 version(WINE) 10 { 11 version = DFL_NoSocket; 12 } 13 14 15 version(DFL_NoSocket) 16 { 17 } 18 else 19 { 20 21 private import dfl.internal.dlib, dfl.internal.clib; 22 23 private 24 { 25 private import std.socket, core.bitop; 26 private import std.c.windows.winsock;//core.sys.windows.winsock; 27 28 alias InternetHost DInternetHost; 29 alias InternetAddress DInternetAddress; 30 31 socket_t getSocketHandle(Socket sock) @nogc nothrow 32 { 33 return sock.handle; 34 } 35 } 36 37 alias std.socket.Socket DflSocket; /// 38 39 private import dfl.internal.winapi, dfl.application, dfl.base, dfl.internal.utf; 40 41 42 private 43 { 44 enum 45 { 46 FD_READ = 0x01, 47 FD_WRITE = 0x02, 48 FD_OOB = 0x04, 49 FD_ACCEPT = 0x08, 50 FD_CONNECT = 0x10, 51 FD_CLOSE = 0x20, 52 FD_QOS = 0x40, 53 FD_GROUP_QOS = 0x80, 54 } 55 56 57 extern(Windows) int WSAAsyncSelect(socket_t s, HWND hWnd, UINT wMsg, int lEvent) nothrow @nogc; 58 } 59 60 61 /// 62 // Can be OR'ed. 63 enum EventType 64 { 65 NONE = 0, /// 66 67 READ = FD_READ, /// ditto 68 WRITE = FD_WRITE, /// ditto 69 OOB = FD_OOB, /// ditto 70 ACCEPT = FD_ACCEPT, /// ditto 71 CONNECT = FD_CONNECT, /// ditto 72 CLOSE = FD_CLOSE, /// ditto 73 74 QOS = FD_QOS, 75 GROUP_QOS = FD_GROUP_QOS, 76 } 77 78 79 /// 80 // -err- will be 0 if no error. 81 // -type- will always contain only one flag. 82 alias void delegate(DflSocket sock, EventType type, int err) RegisterEventCallback; 83 84 85 // Calling this twice on the same socket cancels out previously 86 // registered events for the socket. 87 // Requires Application.run() or Application.doEvents() loop. 88 void registerEvent(DflSocket sock, EventType events, RegisterEventCallback callback) // deprecated 89 { 90 assert(sock !is null, "registerEvent: socket cannot be null"); 91 assert(callback !is null, "registerEvent: callback cannot be null"); 92 93 if(!hwNet) 94 _init(); 95 96 sock.blocking = false; // So the getter will be correct. 97 98 // SOCKET_ERROR 99 if(-1 == WSAAsyncSelect(getSocketHandle(sock), hwNet, WM_DFL_NETEVENT, cast(int)events)) 100 throw new DflException("Unable to register socket events"); 101 102 EventInfo ei; 103 104 ei.sock = sock; 105 ei.callback = callback; 106 allEvents[getSocketHandle(sock)] = ei; 107 } 108 109 110 void unregisterEvent(DflSocket sock) @trusted @nogc nothrow// deprecated 111 { 112 WSAAsyncSelect(getSocketHandle(sock), hwNet, 0, 0); 113 114 //delete allEvents[getSocketHandle(sock)]; 115 if(getSocketHandle(sock) in allEvents) 116 allEvents.remove(getSocketHandle(sock)); 117 } 118 119 120 /// 121 class AsyncSocket: DflSocket // docmain 122 { 123 /// 124 this(AddressFamily af, SocketType type, ProtocolType protocol) 125 { 126 super(af, type, protocol); 127 super.blocking = false; 128 } 129 130 /// ditto 131 this(AddressFamily af, SocketType type) 132 { 133 super(af, type); 134 super.blocking = false; 135 } 136 137 /// ditto 138 this(AddressFamily af, SocketType type, Dstring protocolName) 139 { 140 super(af, type, protocolName); 141 super.blocking = false; 142 } 143 144 /// ditto 145 // For use with accept(). 146 147 protected this() @safe pure nothrow 148 { 149 } 150 151 /// 152 void event(EventType events, RegisterEventCallback callback) 153 { 154 registerEvent(this, events, callback); 155 } 156 157 158 protected override AsyncSocket accepting() 159 { 160 return new AsyncSocket; 161 } 162 163 164 override void close() 165 { 166 unregisterEvent(this); 167 super.close(); 168 } 169 170 171 override @property bool blocking() const // getter 172 { 173 return false; 174 } 175 176 177 override @property void blocking(bool byes) // setter 178 { 179 if(byes) 180 assert(0); 181 } 182 183 } 184 185 186 /// 187 class AsyncTcpSocket: AsyncSocket // docmain 188 { 189 /// 190 this(AddressFamily family) 191 { 192 super(family, SocketType.STREAM, ProtocolType.TCP); 193 } 194 195 /// ditto 196 this() 197 { 198 this(cast(AddressFamily)AddressFamily.INET); 199 } 200 201 /// ditto 202 // Shortcut. 203 this(Address connectTo, EventType events, RegisterEventCallback eventCallback) 204 { 205 this(connectTo.addressFamily()); 206 event(events, eventCallback); 207 connect(connectTo); 208 } 209 } 210 211 212 /// 213 class AsyncUdpSocket: AsyncSocket // docmain 214 { 215 /// 216 this(AddressFamily family) 217 { 218 super(family, SocketType.DGRAM, ProtocolType.UDP); 219 } 220 221 /// ditto 222 this() 223 { 224 this(cast(AddressFamily)AddressFamily.INET); 225 } 226 } 227 228 229 /+ 230 private class GetHostWaitHandle: WaitHandle 231 { 232 this(HANDLE h) 233 { 234 super.handle = h; 235 } 236 237 238 final: 239 240 alias WaitHandle.handle handle; // Overload. 241 242 override @property void handle(HANDLE h) // setter 243 { 244 assert(0); 245 } 246 247 override void close() 248 { 249 WSACancelAsyncRequest(handle); 250 super.handle = INVALID_HANDLE; 251 } 252 253 254 private void _gotEvent() 255 { 256 super.handle = INVALID_HANDLE; 257 } 258 } 259 260 261 private class GetHostAsyncResult, IAsyncResult 262 { 263 this(HANDLE h, GetHostCallback callback) 264 { 265 wh = new GetHostWaitHandle(h); 266 this.callback = callback; 267 } 268 269 270 @property WaitHandle asyncWaitHandle() // getter 271 { 272 return wh; 273 } 274 275 276 @property bool completedSynchronously() // getter 277 { 278 return false; 279 } 280 281 282 @property bool isCompleted() // getter 283 { 284 return wh.handle != WaitHandle.INVALID_HANDLE; 285 } 286 287 288 private: 289 GetHostWaitHandle wh; 290 GetHostCallback callback; 291 292 293 void _gotEvent(LPARAM lparam) 294 { 295 wh._gotEvent(); 296 297 callback(bla, HIWORD(lparam)); 298 } 299 } 300 +/ 301 302 303 private void _getHostErr() 304 { 305 throw new DflException("Get host failure"); // Needs a better message.. ? 306 } 307 308 309 private class _InternetHost: DInternetHost 310 { 311 private: 312 this(void* hostentBytes) 313 { 314 super.validHostent(cast(hostent*)hostentBytes); 315 super.populate(cast(hostent*)hostentBytes); 316 } 317 } 318 319 320 /// 321 // If -err- is nonzero, it is a winsock error code and -inetHost- is null. 322 alias void delegate(DInternetHost inetHost, int err) GetHostCallback; 323 324 325 /// 326 class GetHost // docmain 327 { 328 /// 329 void cancel() 330 { 331 WSACancelAsyncRequest(h); 332 h = null; 333 } 334 335 336 private: 337 HANDLE h; 338 GetHostCallback callback; 339 DThrowable exception; 340 ubyte[/+MAXGETHOSTSTRUCT+/ 1024] hostentBytes; 341 342 343 void _gotEvent(LPARAM lparam) 344 { 345 h = null; 346 347 int err; 348 err = HIWORD(lparam); 349 if(err) 350 callback(null, err); 351 else 352 callback(new _InternetHost(hostentBytes.ptr), 0); 353 } 354 355 356 this() 357 { 358 } 359 } 360 361 362 /// 363 GetHost asyncGetHostByName(Dstring name, GetHostCallback callback) // docmain 364 { 365 if(!hwNet) 366 _init(); 367 368 HANDLE h; 369 GetHost result; 370 371 result = new GetHost; 372 h = WSAAsyncGetHostByName(hwNet, WM_DFL_HOSTEVENT, unsafeStringz(name), 373 cast(char*)result.hostentBytes, result.hostentBytes.length); 374 if(!h) 375 _getHostErr(); 376 377 result.h = h; 378 result.callback = callback; 379 allGetHosts[h] = result; 380 381 return result; 382 } 383 384 385 /// 386 GetHost asyncGetHostByAddr(uint32_t addr, GetHostCallback callback) // docmain 387 { 388 if(!hwNet) 389 _init(); 390 391 HANDLE h; 392 GetHost result; 393 394 result = new GetHost; 395 version(LittleEndian) 396 addr = bswap(addr); 397 h = WSAAsyncGetHostByAddr(hwNet, WM_DFL_HOSTEVENT, cast(char*)&addr, addr.sizeof, 398 AddressFamily.INET, cast(char*)result.hostentBytes, result.hostentBytes.length); 399 if(!h) 400 _getHostErr(); 401 402 result.h = h; 403 result.callback = callback; 404 allGetHosts[h] = result; 405 406 return result; 407 } 408 409 /// ditto 410 // Shortcut. 411 GetHost asyncGetHostByAddr(Dstring addr, GetHostCallback callback) // docmain 412 { 413 uint uiaddr; 414 uiaddr = DInternetAddress.parse(addr); 415 if(DInternetAddress.ADDR_NONE == uiaddr) 416 _getHostErr(); 417 return asyncGetHostByAddr(uiaddr, callback); 418 } 419 420 421 /// 422 class SocketQueue // docmain 423 { 424 /// 425 this(DflSocket sock) 426 in 427 { 428 assert(sock !is null); 429 } 430 body 431 { 432 this.sock = sock; 433 } 434 435 436 /// 437 final @property DflSocket socket() // getter 438 { 439 return sock; 440 } 441 442 443 /// 444 void reset() 445 { 446 writebuf = null; 447 readbuf = null; 448 } 449 450 451 /+ 452 // DMD 0.92 says error: function toString overrides but is not covariant with toString 453 override Dstring toString() 454 { 455 return cast(Dstring)peek(); 456 } 457 +/ 458 459 460 /// 461 void[] peek() 462 { 463 return readbuf[0 .. rpos]; 464 } 465 466 /// ditto 467 void[] peek(uint len) 468 { 469 if(len >= rpos) 470 return peek(); 471 472 return readbuf[0 .. len]; 473 } 474 475 476 /// 477 void[] receive() 478 { 479 ubyte[] result; 480 481 result = readbuf[0 .. rpos]; 482 readbuf = null; 483 rpos = 0; 484 485 return result; 486 } 487 488 /// ditto 489 void[] receive(uint len) 490 { 491 if(len >= rpos) 492 return receive(); 493 494 ubyte[] result; 495 496 result = readbuf[0 .. len]; 497 readbuf = readbuf[len .. readbuf.length]; 498 rpos -= len; 499 500 return result; 501 } 502 503 504 /// 505 void send(void[] buf) 506 { 507 if(canwrite) 508 { 509 assert(!writebuf.length); 510 511 size_t st; 512 if(buf.length > 4096) 513 st = 4096; 514 else 515 st = buf.length; 516 517 st = sock.send(buf[0 .. st]); 518 if(st > 0) 519 { 520 if(buf.length - st) 521 { 522 // dup so it can be appended to. 523 writebuf = (cast(ubyte[])buf)[st .. buf.length].dup; 524 } 525 } 526 else 527 { 528 // dup so it can be appended to. 529 writebuf = (cast(ubyte[])buf).dup; 530 } 531 532 //canwrite = false; 533 } 534 else 535 { 536 writebuf ~= cast(ubyte[])buf; 537 } 538 } 539 540 541 /// 542 // Number of bytes in send queue. 543 @property size_t sendBytes() // getter 544 { 545 return writebuf.length; 546 } 547 548 549 /// 550 // Number of bytes in recv queue. 551 @property uint receiveBytes() // getter 552 { 553 return rpos; 554 } 555 556 557 /// 558 // Same signature as RegisterEventCallback for simplicity. 559 void event(DflSocket _sock, EventType type, int err) 560 in 561 { 562 assert(_sock is sock); 563 } 564 body 565 { 566 switch(type) 567 { 568 case EventType.READ: 569 readEvent(); 570 break; 571 572 case EventType.WRITE: 573 writeEvent(); 574 break; 575 576 default: 577 } 578 } 579 580 581 /// 582 // Call on a read event so that incoming data may be buffered. 583 void readEvent() 584 { 585 if(readbuf.length - rpos < 1024) 586 readbuf.length = readbuf.length + 2048; 587 588 size_t rd = sock.receive(readbuf[rpos .. readbuf.length]); 589 if(rd > 0) 590 rpos += cast(uint)rd; 591 } 592 593 594 /// 595 // Call on a write event so that buffered outgoing data may be sent. 596 void writeEvent() 597 { 598 if(writebuf.length) 599 { 600 ubyte[] buf; 601 602 if(writebuf.length > 4096) 603 buf = writebuf[0 .. 4096]; 604 else 605 buf = writebuf; 606 607 size_t st = sock.send(buf); 608 if(st > 0) 609 writebuf = writebuf[st .. writebuf.length]; 610 } 611 else 612 { 613 //canwrite = true; 614 } 615 } 616 617 618 deprecated 619 { 620 alias receiveBytes recvBytes; 621 alias receive recv; 622 } 623 624 625 private: 626 ubyte[] writebuf; 627 ubyte[] readbuf; 628 uint rpos; 629 DflSocket sock; 630 //bool canwrite = false; 631 632 633 @property bool canwrite() // getter 634 { 635 return writebuf.length == 0; 636 } 637 } 638 639 640 private: 641 642 struct EventInfo 643 { 644 DflSocket sock; 645 RegisterEventCallback callback; 646 DThrowable exception; 647 } 648 649 650 enum UINT WM_DFL_NETEVENT = WM_USER + 104; 651 enum UINT WM_DFL_HOSTEVENT = WM_USER + 105; 652 enum NETEVENT_CLASSNAME = "DFL_NetEvent"; 653 654 EventInfo[socket_t] allEvents; 655 GetHost[HANDLE] allGetHosts; 656 HWND hwNet; 657 658 659 extern(Windows) LRESULT netWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow 660 { 661 switch(msg) 662 { 663 case WM_DFL_NETEVENT: 664 if(cast(socket_t)wparam in allEvents) 665 { 666 EventInfo ei = allEvents[cast(socket_t)wparam]; 667 try 668 { 669 ei.callback(ei.sock, cast(EventType)LOWORD(lparam), HIWORD(lparam)); 670 } 671 catch (DThrowable e) 672 { 673 ei.exception = e; 674 } 675 } 676 break; 677 678 case WM_DFL_HOSTEVENT: 679 if(cast(HANDLE)wparam in allGetHosts) 680 { 681 GetHost gh; 682 gh = allGetHosts[cast(HANDLE)wparam]; 683 assert(gh !is null); 684 //delete allGetHosts[cast(HANDLE)wparam]; 685 allGetHosts.remove(cast(HANDLE)wparam); 686 try 687 { 688 gh._gotEvent(lparam); 689 } 690 catch (DThrowable e) 691 { 692 gh.exception = e; 693 } 694 } 695 break; 696 697 default: 698 } 699 700 return 1; 701 } 702 703 704 void _init() 705 { 706 WNDCLASSEXA wce; 707 wce.cbSize = wce.sizeof; 708 wce.lpszClassName = NETEVENT_CLASSNAME.ptr; 709 wce.lpfnWndProc = &netWndProc; 710 wce.hInstance = GetModuleHandleA(null); 711 712 if(!RegisterClassExA(&wce)) 713 { 714 debug(APP_PRINT) 715 cprintf("RegisterClassEx() failed for network event class.\n"); 716 717 init_err: 718 throw new DflException("Unable to initialize asynchronous socket library"); 719 } 720 721 hwNet = CreateWindowExA(0, NETEVENT_CLASSNAME.ptr, "", 0, 0, 0, 0, 0, HWND_MESSAGE, null, wce.hInstance, null); 722 if(!hwNet) 723 { 724 // Guess it doesn't support HWND_MESSAGE, so just try null parent. 725 726 hwNet = CreateWindowExA(0, NETEVENT_CLASSNAME.ptr, "", 0, 0, 0, 0, 0, null, null, wce.hInstance, null); 727 if(!hwNet) 728 { 729 debug(APP_PRINT) 730 cprintf("CreateWindowEx() failed for network event window.\n"); 731 732 goto init_err; 733 } 734 } 735 } 736 737 } 738