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