1 module libasync.udp; 2 3 import libasync.types; 4 5 import libasync.events; 6 7 /// Wrapper for a UDP Stream which must be bound to a socket. 8 final nothrow class AsyncUDPSocket 9 { 10 nothrow: 11 private: 12 EventLoop m_evLoop; 13 fd_t m_socket; 14 NetworkAddress m_local; 15 16 public: 17 this(EventLoop evl) 18 in { assert(evl !is null); } 19 body { m_evLoop = evl; } 20 21 mixin DefStatus; 22 23 24 /// Returns the locally bound address as an OS-specific structure. 25 @property NetworkAddress local() const 26 { 27 return m_local; 28 } 29 30 /// Grants broadcast permissions to the socket (must be set before run). 31 bool broadcast(bool b) 32 in { assert(m_socket == fd_t.init, "Cannot change state on unbound UDP socket"); } 33 body { 34 return m_evLoop.broadcast(m_socket, b); 35 } 36 37 /// Sets the hostname and port to which the UDP socket must be bound locally. 38 typeof(this) host(string hostname, size_t port) 39 in { assert(m_socket == fd_t.init, "Cannot rebind an UDP socket"); } 40 body 41 { 42 m_local = m_evLoop.resolveHost(hostname, cast(ushort) port); 43 return this; 44 } 45 46 /// Sets the IP and port to which the UDP socket will be bound locally. 47 typeof(this) ip(string ip, size_t port) 48 in { assert(m_socket == fd_t.init, "Cannot rebind an UDP socket"); } 49 body { 50 m_local = m_evLoop.resolveIP(ip, cast(ushort) port); 51 return this; 52 } 53 54 /// Sets the local network address to which this UDP Socket will be bound. 55 @property void local(NetworkAddress l) 56 in { 57 assert(l != NetworkAddress.init, "The local address is empty"); 58 assert(m_socket == fd_t.init, "Cannot rebind an UDP socket"); 59 } 60 body { 61 m_local = l; 62 } 63 64 /// Registers the UDP socket in the underlying OS event loop, forwards 65 /// all related events to the specified delegate. 66 bool run(void delegate(UDPEvent) del) 67 { 68 UDPHandler handler; 69 handler.del = del; 70 handler.conn = this; 71 return run(handler); 72 } 73 74 private bool run(UDPHandler del) 75 in { assert(m_local != NetworkAddress.init && m_socket == fd_t.init, "Cannot rebind an UDP socket"); } 76 body { 77 m_socket = m_evLoop.run(this, del); 78 if (m_socket == fd_t.init) 79 return false; 80 else 81 return true; 82 } 83 84 /// Receives data from one peer and copies its address to the 85 /// associated OS-specific address structure. 86 uint recvFrom(ref ubyte[] data, ref NetworkAddress addr) { 87 return m_evLoop.recvFrom(m_socket, data, addr); 88 } 89 90 /// Sends data to the internet address specified by the associated 91 /// OS-specific structure. 92 uint sendTo(in ubyte[] data, in NetworkAddress addr) { 93 return m_evLoop.sendTo(m_socket, data, addr); 94 } 95 96 /// Cleans up the resources associated with this object in the underlying OS. 97 bool kill() 98 in { assert(m_socket != fd_t.init); } 99 body { 100 return m_evLoop.kill(this); 101 } 102 103 package: 104 version(Posix) mixin EvInfoMixins; 105 106 @property fd_t socket() const { 107 return m_socket; 108 } 109 110 @property void socket(fd_t val) { 111 m_socket = val; 112 } 113 114 } 115 116 package struct UDPHandler { 117 AsyncUDPSocket conn; 118 void delegate(UDPEvent) del; 119 void opCall(UDPEvent code){ 120 assert(conn !is null); 121 del(code); 122 assert(conn !is null); 123 return; 124 } 125 } 126 127 enum UDPEvent : char { 128 ERROR = 0, 129 READ, 130 WRITE 131 }