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  * A listener socket is a special type of socket that listens to a given port
27  * and waits for connections on that port. This is all it can do.
28  *
29  * When a new connection is received, you must call `accept` and the listener
30  * returns a new instance of $(TCPSOCKET_LINK) that is properly initialized and
31  * can be used to communicate with the new client.
32  *
33  * Listener sockets are specific to the TCP protocol, UDP sockets are
34  * connectionless and can therefore communicate directly. As a consequence, a
35  * listener socket will always return the new connections as $(TCPSOCKET_LINK)
36  * instances.
37  *
38  * A listener is automatically closed on destruction, like all other types of
39  * socket. However if you want to stop listening before the socket is destroyed,
40  * you can call its `close()` function.
41  *
42  * Example:
43  * ---
44  * // Create a listener socket and make it wait for new
45  * // connections on port 55001
46  * auto listener = new TcpListener();
47  * listener.listen(55001);
48  *
49  * // Endless loop that waits for new connections
50  * while (running)
51  * {
52  *     auto client = new TcpSocket();
53  *     if (listener.accept(client) == Socket.Status.Done)
54  *     {
55  *         // A new client just connected!
56  *         writeln("New connection received from ", client.getRemoteAddress());
57  *         doSomethingWith(client);
58  *     }
59  * }
60  * ---
61  *
62  * See_Also:
63  * $(TCPSOCKET_LINK), $(SOCKET_LINK)
64  */
65 module dsfml.network.tcplistener;
66 
67 
68 import dsfml.network.ipaddress;
69 import dsfml.network.socket;
70 import dsfml.network.tcpsocket;
71 import dsfml.system.err;
72 
73 /**
74  * Socket that listens to new TCP connections.
75  */
76 class TcpListener:Socket
77 {
78     package sfTcpListener* sfPtr;
79 
80     /// Default constructor.
81     this()
82     {
83         sfPtr = sfTcpListener_create();
84     }
85 
86     /// Destructor.
87     ~this()
88     {
89         import dsfml.system.config;
90         mixin(destructorOutput);
91         sfTcpListener_destroy(sfPtr);
92     }
93 
94     /**
95      * Get the port to which the socket is bound locally.
96      *
97      * If the socket is not listening to a port, this function returns 0.
98      *
99      * Returns: Port to which the socket is bound.
100      */
101     ushort getLocalPort()
102     {
103         return sfTcpListener_getLocalPort(sfPtr);
104     }
105 
106     /**
107      * Tell whether the socket is in blocking or non-blocking mode.
108      *
109      * In blocking mode, calls will not return until they have completed their
110      * task. For example, a call to `receive` in blocking mode won't return
111      * until some data was actually received.
112      *
113      * In non-blocking mode, calls will
114      * always return immediately, using the return code to signal whether there
115      * was data available or not. By default, all sockets are blocking.
116      *
117      * Params:
118      *  blocking = true to set the socket as blocking, false for non-blocking
119      */
120     void setBlocking(bool blocking)
121     {
122         sfTcpListener_setBlocking(sfPtr, blocking);
123     }
124 
125     /**
126      * Accept a new connection.
127      *
128      * If the socket is in blocking mode, this function will not return until a
129      * connection is actually received.
130      *
131      * Params:
132      *  socket = Socket that will hold the new connection
133      *
134      * Returns: Status code.
135      */
136     Status accept(TcpSocket socket)
137     {
138         import dsfml.system..string;
139 
140         Status toReturn = sfTcpListener_accept(sfPtr, socket.sfPtr);
141         err.write(dsfml.system..string.toString(sfErr_getOutput()));
142         return toReturn;
143     }
144 
145     /**
146      * Start listening for connections.
147      *
148      * This functions makes the socket listen to the specified port, waiting for
149      * new connections. If the socket was previously listening to another port,
150      * it will be stopped first and bound to the new port.
151      *
152      * Params:
153      *  port = Port to listen for new connections
154      *
155      * Returns: Status code.
156      */
157     Status listen(ushort port, IpAddress address = IpAddress.Any)
158     {
159         import dsfml.system..string;
160 
161         Status toReturn = sfTcpListener_listen(sfPtr, port, &address);
162         err.write(dsfml.system..string.toString(sfErr_getOutput()));
163         return toReturn;
164     }
165 
166     /**
167      * Tell whether the socket is in blocking or non-blocking mode.
168      *
169      * Returns: true if the socket is blocking, false otherwise.
170      */
171     bool isBlocking() const
172     {
173         return (sfTcpListener_isBlocking(sfPtr));
174     }
175 
176 }
177 
178 unittest
179 {
180     version(DSFML_Unittest_Network)
181     {
182         import std.stdio;
183         import dsfml.network.ipaddress;
184 
185         writeln("Unittest for Listener");
186         //socket connecting to server
187         auto clientSocket = new TcpSocket();
188 
189         //listener looking for new sockets
190         auto listener = new TcpListener();
191         listener.listen(55002);
192 
193         writeln("The listener is listening to port ", listener.getLocalPort());
194 
195         //get our client socket to connect to the server
196         clientSocket.connect(IpAddress.LocalHost, 55002);
197 
198 
199         //socket on the server side connected to the client's socket
200         auto serverSocket = new TcpSocket();
201 
202         //accepts a new connection and binds it to the socket in the parameter
203         listener.accept(serverSocket);
204 
205         clientSocket.disconnect();
206         writeln();
207     }
208 }
209 
210 package extern(C):
211 
212 struct sfTcpListener;
213 
214 sfTcpListener* sfTcpListener_create();
215 
216 
217 //Destroy a TCP listener
218 void sfTcpListener_destroy(sfTcpListener* listener);
219 
220 
221 //Set the blocking state of a TCP listener
222 void sfTcpListener_setBlocking(sfTcpListener* listener, bool blocking);
223 
224 
225 //Tell whether a TCP listener is in blocking or non-blocking mode
226 bool sfTcpListener_isBlocking(const sfTcpListener* listener);
227 
228 
229 //Get the port to which a TCP listener is bound locally
230 ushort sfTcpListener_getLocalPort(const(sfTcpListener)* listener);
231 
232 
233 //Start listening for connections
234 Socket.Status sfTcpListener_listen(sfTcpListener* listener, ushort port, IpAddress* address);
235 
236 
237 //Accept a new connection
238 Socket.Status sfTcpListener_accept(sfTcpListener* listener, sfTcpSocket* connected);
239 
240 const(char)* sfErr_getOutput();