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