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);