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