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