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