1 /* 2 * DSFML - The Simple and Fast Multimedia Library for D 3 * 4 * Copyright (c) 2013 - 2018 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 8 * use of this software. 9 * 10 * Permission is granted to anyone to use this software for any purpose, 11 * including commercial applications, and to alter it and redistribute it 12 * freely, subject to the following restrictions: 13 * 14 * 1. The origin of this software must not be misrepresented; you must not claim 15 * that you wrote the original software. If you use this software in a product, 16 * an acknowledgment in the product documentation would be appreciated but is 17 * not required. 18 * 19 * 2. Altered source versions must be plainly marked as such, and must not be 20 * misrepresented as being the original software. 21 * 22 * 3. This notice may not be removed or altered from any source distribution 23 * 24 * 25 * DSFML is based on SFML (Copyright Laurent Gomila) 26 */ 27 28 /** 29 * Socket selectors provide a way to wait until some data is available on a set 30 * of sockets, instead of just one. This is convenient when you have multiple 31 * sockets that may possibly receive data, but you don't know which one will be 32 * ready first. In particular, it avoids to use a thread for each socket; with 33 * selectors, a single thread can handle all the sockets. 34 * 35 * All types of sockets can be used in a selector: 36 * $(UL 37 * $(LI $(TCPLISTENER_LINK)) 38 * $(LI $(TCPSOCKET_LINK)) 39 * $(LI $(UDPSOCKET_LINK))) 40 * 41 * $(PARA 42 * A selector doesn't store its own copies of the sockets, it simply keeps a 43 * reference to the original sockets that you pass to the "add" function. 44 * Therefore, you can't use the selector as a socket container, you must store 45 * them outside and make sure that they are alive as long as they are used in 46 * the selector (i.e., they cannot be collected by the GC). 47 * 48 * Using a selector is simple:) 49 * $(UL 50 * $(LI populate the selector with all the sockets that you want to observe) 51 * $(LI make it wait until there is data available on any of the sockets) 52 * $(LI test each socket to find out which ones are ready)) 53 * 54 * Example: 55 * --- 56 * // Create a socket to listen to new connections 57 * auto listener = new TcpListener(); 58 * listener.listen(55001); 59 * 60 * // Create a list to store the future clients 61 * TcpSocket[] clients; 62 * 63 * // Create a selector 64 * auto selector = new SocketSelector(); 65 * 66 * // Add the listener to the selector 67 * selector.add(listener); 68 * 69 * // Endless loop that waits for new connections 70 * while (running) 71 * { 72 * // Make the selector wait for data on any socket 73 * if (selector.wait()) 74 * { 75 * // Test the listener 76 * if (selector.isReady(listener)) 77 * { 78 * // The listener is ready: there is a pending connection 79 * auto client = new TcpSocket(); 80 * if (listener.accept(client) == Socket.Status.Done) 81 * { 82 * // Add the new client to the clients list 83 * clients~=client; 84 * 85 * // Add the new client to the selector so that we will 86 * // be notified when he sends something 87 * selector.add(client); 88 * } 89 * else 90 * { 91 * // Error, we won't get a new connection 92 * } 93 * } 94 * else 95 * { 96 * // The listener socket is not ready, test all other sockets (the clients) 97 * foreach(client; clients) 98 * { 99 * if (selector.isReady(client)) 100 * { 101 * // The client has sent some data, we can receive it 102 * auto packet = new Packet(); 103 * if (client.receive(packet) == Socket.Status.Done) 104 * { 105 * ... 106 * } 107 * } 108 * } 109 * } 110 * } 111 * } 112 * --- 113 * 114 * See_Also: 115 * $(SOCKET_LINK) 116 */ 117 module nudsfml.network.socketselector; 118 119 import nudsfml.network.tcplistener; 120 import nudsfml.network.tcpsocket; 121 import nudsfml.network.udpsocket; 122 123 public import nudsfml.system.time; 124 125 /** 126 * Multiplexer that allows to read from multiple sockets. 127 */ 128 class SocketSelector 129 { 130 package sfSocketSelector* sfPtr; 131 132 /// Default constructor. 133 this() 134 { 135 sfPtr = sfSocketSelector_create(); 136 } 137 138 /// Destructor. 139 ~this() 140 { 141 import nudsfml.system.config; 142 mixin(destructorOutput); 143 sfSocketSelector_destroy(sfPtr); 144 } 145 146 /** 147 * Add a new TcpListener to the selector. 148 * 149 * This function keeps a weak reference to the socket, so you have to make 150 * sure that the socket is not destroyed while it is stored in the selector. 151 * This function does nothing if the socket is not valid. 152 * 153 * Params: 154 * listener = Reference to the listener to add 155 */ 156 void add(TcpListener listener) 157 { 158 sfSocketSelector_addTcpListener(sfPtr, listener.sfPtr); 159 } 160 161 /** 162 * Add a new TcpSocket to the selector. 163 * 164 * This function keeps a weak reference to the socket, so you have to make 165 * sure that the socket is not destroyed while it is stored in the selector. 166 * This function does nothing if the socket is not valid. 167 * 168 * Params: 169 * socket = Reference to the socket to add 170 */ 171 void add(TcpSocket socket) 172 { 173 sfSocketSelector_addTcpSocket(sfPtr, socket.sfPtr); 174 } 175 176 /** 177 * Add a new UdpSocket to the selector. 178 * 179 * This function keeps a weak reference to the socket, so you have to make 180 * sure that the socket is not destroyed while it is stored in the selector. 181 * This function does nothing if the socket is not valid. 182 * 183 * Params: 184 * socket = Reference to the socket to add 185 */ 186 void add(UdpSocket socket) 187 { 188 sfSocketSelector_addUdpSocket(sfPtr, socket.sfPtr); 189 } 190 191 /** 192 * Remove all the sockets stored in the selector. 193 * 194 * This function doesn't destroy any instance, it simply removes all the 195 * references that the selector has to external sockets. 196 */ 197 void clear() 198 { 199 sfSocketSelector_clear(sfPtr); 200 } 201 202 /** 203 * Test a socket to know if it is ready to receive data. 204 * 205 * This function must be used after a call to Wait, to know which sockets 206 * are ready to receive data. If a socket is ready, a call to receive will 207 * never block because we know that there is data available to read. Note 208 * that if this function returns true for a TcpListener, this means that it 209 * is ready to accept a new connection. 210 */ 211 bool isReady(TcpListener listener) const 212 { 213 return (sfSocketSelector_isTcpListenerReady(sfPtr, listener.sfPtr)); 214 } 215 216 /// ditto 217 bool isReady(TcpSocket socket) const 218 { 219 return (sfSocketSelector_isTcpSocketReady(sfPtr, socket.sfPtr)); 220 } 221 222 /// ditto 223 bool isReady(UdpSocket socket) const 224 { 225 return (sfSocketSelector_isUdpSocketReady(sfPtr, socket.sfPtr)); 226 } 227 228 /** 229 * Remove a socket from the selector. 230 * 231 * This function doesn't destroy the socket, it simply removes the reference 232 * that the selector has to it. 233 * 234 * Params: 235 * socket = Reference to the socket to remove 236 */ 237 void remove(TcpListener socket) 238 { 239 sfSocketSelector_removeTcpListener(sfPtr, socket.sfPtr); 240 } 241 242 /** 243 * Remove a socket from the selector. 244 * 245 * This function doesn't destroy the socket, it simply removes the reference 246 * that the selector has to it. 247 * 248 * Params: 249 * socket = Reference to the socket to remove 250 */ 251 void remove(TcpSocket socket) 252 { 253 sfSocketSelector_removeTcpSocket(sfPtr, socket.sfPtr); 254 } 255 256 /** 257 * Remove a socket from the selector. 258 * 259 * This function doesn't destroy the socket, it simply removes the reference 260 * that the selector has to it. 261 * 262 * Params: 263 * socket = Reference to the socket to remove 264 */ 265 void remove(UdpSocket socket) 266 { 267 sfSocketSelector_removeUdpSocket(sfPtr, socket.sfPtr); 268 } 269 270 /** 271 * Wait until one or more sockets are ready to receive. 272 * 273 * This function returns as soon as at least one socket has some data 274 * available to be received. To know which sockets are ready, use the 275 * isReady function. If you use a timeout and no socket is ready before the 276 * timeout is over, the function returns false. 277 * 278 * Parameters 279 * timeout = Maximum time to wait, (use Time.Zero for infinity) 280 * 281 * Returns: true if there are sockets ready, false otherwise. 282 */ 283 bool wait(Time timeout = Time.Zero) 284 { 285 return (sfSocketSelector_wait(sfPtr, timeout.asMicroseconds())); 286 } 287 } 288 289 unittest 290 { 291 version(DSFML_Unittest_Network) 292 { 293 import std.stdio; 294 import nudsfml.network.ipaddress; 295 296 writeln("Unittest for SocketSelector"); 297 298 auto selector = new SocketSelector(); 299 300 //get a listener and start listening to a new port 301 auto listener = new TcpListener(); 302 listener.listen(55004); 303 304 //add the listener to the selector 305 selector.add(listener); 306 307 //The client tries to connect to the server 308 auto clientSocket = new TcpSocket(); 309 clientSocket.connect(IpAddress.LocalHost, 55004); 310 311 //wait for the selector to be informed of new things! 312 selector.wait(); 313 314 auto serverSocket = new TcpSocket(); 315 //the listener is ready! New connections are available 316 if(selector.isReady(listener)) 317 { 318 writeln("Accepted the connection."); 319 listener.accept(serverSocket); 320 } 321 322 writeln(); 323 } 324 } 325 326 private extern(C): 327 328 struct sfSocketSelector; 329 330 //Create a new selector 331 sfSocketSelector* sfSocketSelector_create(); 332 333 //Create a new socket selector by copying an existing one 334 sfSocketSelector* sfSocketSelector_copy(const sfSocketSelector* selector); 335 336 //Destroy a socket selector 337 void sfSocketSelector_destroy(sfSocketSelector* selector); 338 339 //Add a new socket to a socket selector 340 void sfSocketSelector_addTcpListener(sfSocketSelector* selector, sfTcpListener* socket); 341 void sfSocketSelector_addTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket); 342 void sfSocketSelector_addUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket); 343 344 //Remove a socket from a socket selector 345 void sfSocketSelector_removeTcpListener(sfSocketSelector* selector, sfTcpListener* socket); 346 void sfSocketSelector_removeTcpSocket(sfSocketSelector* selector, sfTcpSocket* socket); 347 void sfSocketSelector_removeUdpSocket(sfSocketSelector* selector, sfUdpSocket* socket); 348 349 //Remove all the sockets stored in a selector 350 void sfSocketSelector_clear(sfSocketSelector* selector); 351 352 //Wait until one or more sockets are ready to receive 353 bool sfSocketSelector_wait(sfSocketSelector* selector, long timeout); 354 355 //Test a socket to know if it is ready to receive data 356 bool sfSocketSelector_isTcpListenerReady(const(sfSocketSelector)* selector, sfTcpListener* socket); 357 bool sfSocketSelector_isTcpSocketReady(const(sfSocketSelector)* selector, sfTcpSocket* socket); 358 bool sfSocketSelector_isUdpSocketReady(const(sfSocketSelector)* selector, sfUdpSocket* socket);