1 /* 2 DSFML - The Simple and Fast Multimedia Library for D 3 4 Copyright (c) 2013 - 2015 Jeremy DeHaan (dehaan.jeremiah@gmail.com) 5 6 This software is provided 'as-is', without any express or implied warranty. 7 In no event will the authors be held liable for any damages arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, including commercial applications, 10 and to alter it and redistribute it freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 14 15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 16 17 3. This notice may not be removed or altered from any source distribution 18 */ 19 20 ///A module contianing the SocketSelector class 21 module dsfml.network.socketselector; 22 23 import dsfml.network.tcplistener; 24 import dsfml.network.tcpsocket; 25 import dsfml.network.udpsocket; 26 27 import core.time; 28 29 /** 30 *Multiplexer that allows to read from multiple sockets. 31 * 32 *Socket selectors provide a way to wait until some data is available on a set of sockets, instead of just one. 33 * 34 *This is convenient when you have multiple sockets that may possibly receive data, but you don't know which one will be ready first. 35 *In particular, it avoids to use a thread for each socket; with selectors, a single thread can handle all the sockets. 36 */ 37 class SocketSelector 38 { 39 package sfSocketSelector* sfPtr; 40 41 ///Default constructor 42 this() 43 { 44 sfPtr = sfSocketSelector_create(); 45 } 46 47 ///Destructor 48 ~this() 49 { 50 import dsfml.system.config; 51 mixin(destructorOutput); 52 sfSocketSelector_destroy(sfPtr); 53 } 54 55 //Add a new TcpListener to the selector. 56 /// 57 ///This function keeps a weak reference to the socket, so you have to make sure that the socket is not destroyed while it is stored in the selector. This function does nothing if the socket is not valid. 58 /// 59 ///Params: 60 /// listener = Reference to the listener to add. 61 void add(TcpListener listener) 62 { 63 sfSocketSelector_addTcpListener(sfPtr, listener.sfPtr); 64 } 65 66 ///Add a new TcpSocket to the selector. 67 /// 68 ///This function keeps a weak reference to the socket, so you have to make sure that the socket is not destroyed while it is stored in the selector. This function does nothing if the socket is not valid. 69 /// 70 ///Params: 71 /// socket = Reference to the socket to add, 72 void add(TcpSocket socket) 73 { 74 sfSocketSelector_addTcpSocket(sfPtr, socket.sfPtr); 75 } 76 77 ///Add a new UdpSocket to the selector. 78 /// 79 ///This function keeps a weak reference to the socket, so you have to make sure that the socket is not destroyed while it is stored in the selector. This function does nothing if the socket is not valid. 80 /// 81 ///Params: 82 /// socket = Reference to the socket to add, 83 void add(UdpSocket socket) 84 { 85 sfSocketSelector_addUdpSocket(sfPtr, socket.sfPtr); 86 } 87 88 ///Remove all the sockets stored in the selector. 89 /// 90 ///This function doesn't destroy any instance, it simply removes all the references that the selector has to external sockets. 91 void clear() 92 { 93 sfSocketSelector_clear(sfPtr); 94 } 95 96 ///Test a socket to know if it is ready to receive data. 97 /// 98 ///This function must be used after a call to Wait, to know which sockets are ready to receive data. If a socket is ready, a call to receive will never block because we know that there is data available to read. Note that if this function returns true for a TcpListener, this means that it is ready to accept a new connection. 99 /// 100 bool isReady(TcpListener listener) 101 { 102 return (sfSocketSelector_isTcpListenerReady(sfPtr, listener.sfPtr)); 103 } 104 105 ///Test a socket to know if it is ready to receive data. 106 /// 107 ///This function must be used after a call to Wait, to know which sockets are ready to receive data. If a socket is ready, a call to receive will never block because we know that there is data available to read. Note that if this function returns true for a TcpListener, this means that it is ready to accept a new connection. 108 /// 109 bool isReady(TcpSocket socket) 110 { 111 return (sfSocketSelector_isTcpSocketReady(sfPtr, socket.sfPtr)); 112 } 113 114 ///Test a socket to know if it is ready to receive data. 115 /// 116 ///This function must be used after a call to Wait, to know which sockets are ready to receive data. If a socket is ready, a call to receive will never block because we know that there is data available to read. Note that if this function returns true for a TcpListener, this means that it is ready to accept a new connection. 117 /// 118 bool isReady(UdpSocket socket) 119 { 120 return (sfSocketSelector_isUdpSocketReady(sfPtr, socket.sfPtr)); 121 } 122 123 ///Remove a socket from the selector. 124 /// 125 ///This function doesn't destroy the socket, it simply removes the reference that the selector has to it. 126 /// 127 ///Parameters 128 /// socket = Reference to the socket to remove. 129 void remove(TcpListener socket) 130 { 131 sfSocketSelector_removeTcpListener(sfPtr, socket.sfPtr); 132 } 133 134 ///ditto 135 void remove(TcpSocket socket) 136 { 137 sfSocketSelector_removeTcpSocket(sfPtr, socket.sfPtr); 138 } 139 140 ///ditto 141 void remove(UdpSocket socket) 142 { 143 sfSocketSelector_removeUdpSocket(sfPtr, socket.sfPtr); 144 } 145 146 ///Wait until one or more sockets are ready to receive. 147 /// 148 ///This function returns as soon as at least one socket has some data available to be received. To know which sockets are ready, use the isReady function. If you use a timeout and no socket is ready before the timeout is over, the function returns false. 149 /// 150 ///Parameters 151 /// timeout = Maximum time to wait, (use Time::Zero for infinity). 152 /// 153 ///Returns: True if there are sockets ready, false otherwise. 154 bool wait(Duration timeout = Duration.zero()) 155 { 156 return (sfSocketSelector_wait(sfPtr, timeout.total!"usecs")); 157 } 158 } 159 160 unittest 161 { 162 version(DSFML_Unittest_Network) 163 { 164 import std.stdio; 165 import dsfml.network.ipaddress; 166 167 168 writeln("Unittest for SocketSelector"); 169 170 auto selector = new SocketSelector(); 171 172 //get a listener and start listening to a new port 173 auto listener = new TcpListener(); 174 listener.listen(55004); 175 176 //add the listener to the selector 177 selector.add(listener); 178 179 //The client tries to connect to the server 180 auto clientSocket = new TcpSocket(); 181 clientSocket.connect(IpAddress.LocalHost, 55004); 182 183 184 //wait for the selector to be informed of new things! 185 selector.wait(); 186 187 auto serverSocket = new TcpSocket(); 188 //the listener is ready! New connections are available 189 if(selector.isReady(listener)) 190 { 191 writeln("Accepted the connection."); 192 listener.accept(serverSocket); 193 } 194 195 196 197 writeln(); 198 } 199 } 200 201 private extern(C): 202 203 struct sfSocketSelector; 204 205 //Create a new selector 206 sfSocketSelector* sfSocketSelector_create(); 207 208 //Create a new socket selector by copying an existing one 209 sfSocketSelector* sfSocketSelector_copy(const sfSocketSelector* selector); 210 211 212 //Destroy a socket selector 213 void sfSocketSelector_destroy(sfSocketSelector* selector); 214 215 216 //Add a new socket to a socket selector 217 void sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket); 218 void sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket); 219 void sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket); 220 221 222 //Remove a socket from a socket selector 223 void sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket); 224 void sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket); 225 void sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket); 226 227 228 //Remove all the sockets stored in a selector 229 void sfSocketSelector_clear(sfSocketSelector* selector); 230 231 232 //Wait until one or more sockets are ready to receive 233 bool sfSocketSelector_wait(sfSocketSelector* selector, long timeout); 234 235 236 //Test a socket to know if it is ready to receive data 237 bool sfSocketSelector_isTcpListenerReady(const(sfSocketSelector)* selector, sfTcpListener* socket); 238 bool sfSocketSelector_isTcpSocketReady(const(sfSocketSelector)* selector, sfTcpSocket* socket); 239 bool sfSocketSelector_isUdpSocketReady(const(sfSocketSelector)* selector, sfUdpSocket* socket);