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