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  * TCP is a connected protocol, which means that a TCP socket can only
27  * communicate with the host it is connected to.
28  *
29  * It can't send or receive anything if it is not connected.
30  *
31  * The TCP protocol is reliable but adds a slight overhead. It ensures that your
32  * data will always be received in order and without errors (no data corrupted,
33  * lost or duplicated).
34  *
35  * When a socket is connected to a remote host, you can retrieve informations
36  * about this host with the `getRemoteAddress` and `getRemotePort` functions.
37  *
38  * You can also get the local port to which the socket is bound (which is
39  * automatically chosen when the socket is connected), with the `getLocalPort`
40  * function.
41  *
42  * Sending and receiving data can use either the low-level or the high-level
43  * functions. The low-level functions process a raw sequence of bytes, and
44  * cannot ensure that one call to Send will exactly match one call to Receive at
45  * the other end of the socket.
46  *
47  * The high-level interface uses packets (see $(PACKET_LINK)), which are easier
48  * to use and provide more safety regarding the data that is exchanged. You can
49  * look at the $(PACKET_LINK) class to get more details about how they work.
50  *
51  * The socket is automatically disconnected when it is destroyed, but if you
52  * want to explicitely close the connection while the socket instance is still
53  * alive, you can call disconnect.
54  *
55  * Example:
56  * ---
57  * // ----- The client -----
58  *
59  * // Create a socket and connect it to 192.168.1.50 on port 55001
60  * auto socket = new TcpSocket();
61  * socket.connect("192.168.1.50", 55001);
62  *
63  * // Send a message to the connected host
64  * string message = "Hi, I am a client";
65  * socket.send(message);
66  *
67  * // Receive an answer from the server
68  * char[1024] buffer;
69  * size_t received = 0;
70  * socket.receive(buffer, received);
71  * writeln("The server said: ", buffer[0 .. received]);
72  *
73  * // ----- The server -----
74  *
75  * // Create a listener to wait for incoming connections on port 55001
76  * auto listener = TcpListener();
77  * listener.listen(55001);
78  *
79  * // Wait for a connection
80  * auto socket = new TcpSocket();
81  * listener.accept(socket);
82  * writeln("New client connected: ", socket.getRemoteAddress());
83  *
84  * // Receive a message from the client
85  * char[1024] buffer;
86  * size_t received = 0;
87  * socket.receive(buffer, received);
88  * writeln("The client said: ", buffer[0 .. received]);
89  *
90  * // Send an answer
91  * string message = "Welcome, client";
92  * socket.send(message);
93  * ---
94  *
95  * See_Also:
96  * $(SOCKET_LINK), $(UDPSOCKET_LINK), $(PACKET_LINK)
97  */
98 module dsfml.network.tcpsocket;
99 
100 import core.time;
101 
102 import dsfml.network.ipaddress;
103 import dsfml.network.packet;
104 import dsfml.network.socket;
105 
106 import dsfml.system.err;
107 
108 /**
109  * Specialized socket using the TCP protocol.
110  */
111 class TcpSocket:Socket
112 {
113     package sfTcpSocket* sfPtr;
114 
115     /// Default constructor.
116     this()
117     {
118         sfPtr = sfTcpSocket_create();
119     }
120 
121     /// Destructor.
122     ~this()
123     {
124         import dsfml.system.config;
125         mixin(destructorOutput);
126         sfTcpSocket_destroy(sfPtr);
127     }
128 
129     /**
130      * Get the port to which the socket is bound locally.
131      *
132      * If the socket is not connected, this function returns 0.
133      *
134      * Returns: Port to which the socket is bound.
135      */
136     ushort getLocalPort()
137     {
138         return sfTcpSocket_getLocalPort(sfPtr);
139     }
140 
141     /**
142      * Get the address of the connected peer.
143      *
144      * It the socket is not connected, this function returns `IpAddress.None`.
145      *
146      * Returns: Address of the remote peer.
147      */
148     IpAddress getRemoteAddress()
149     {
150         IpAddress temp;
151 
152         sfTcpSocket_getRemoteAddress(sfPtr,&temp);
153 
154         return temp;
155     }
156 
157     /**
158      * Get the port of the connected peer to which the socket is connected.
159      *
160      * If the socket is not connected, this function returns 0.
161      *
162      * Returns: Remote port to which the socket is connected.
163      */
164     ushort getRemotePort()
165     {
166         return sfTcpSocket_getRemotePort(sfPtr);
167     }
168 
169     /**
170      * Set the blocking state of the socket.
171      *
172      * In blocking mode, calls will not return until they have completed their
173      * task. For example, a call to `receive` in blocking mode won't return
174      * until some data was actually received.
175      *
176      * In non-blocking mode, calls will always return immediately, using the
177      * return code to signal whether there was data available or not. By
178      * default, all sockets are blocking.
179      *
180      * Params:
181      *  blocking = true to set the socket as blocking, false for non-blocking
182      */
183     void setBlocking(bool blocking)
184     {
185         sfTcpSocket_setBlocking(sfPtr, blocking);
186     }
187 
188     /**
189      * Connect the socket to a remote peer.
190      *
191      * In blocking mode, this function may take a while, especially if the
192      * remote peer is not reachable. The last parameter allows you to stop
193      * trying to connect after a given timeout.
194      *
195      * If the socket was previously connected, it is first disconnected.
196      *
197      * Params:
198      *  host    = Address of the remote peer
199      * 	port    = Port of the remote peer
200      * 	timeout = Optional maximum time to wait
201      *
202      * Returns: Status code.
203      */
204     Status connect(IpAddress host, ushort port, Duration timeout = Duration.zero())
205     {
206         return sfTcpSocket_connect(sfPtr, &host, port, timeout.total!"usecs");
207     }
208 
209     /**
210      * Disconnect the socket from its remote peer.
211      *
212      * This function gracefully closes the connection. If the socket is not
213      * connected, this function has no effect.
214      */
215     void disconnect()
216     {
217         sfTcpSocket_disconnect(sfPtr);
218     }
219 
220     /**
221      * Tell whether the socket is in blocking or non-blocking mode.
222      *
223      * Returns: true if the socket is blocking, false otherwise.
224      */
225     bool isBlocking() const
226     {
227         return (sfTcpSocket_isBlocking(sfPtr));
228     }
229 
230     /**
231      * Send raw data to the remote peer.
232      *
233      * This function will fail if the socket is not connected.
234      *
235      * Params:
236      * 	data = Sequence of bytes to send
237      *
238      * Returns: Status code.
239      */
240     Status send(const(void)[] data)
241     {
242         import dsfml.system..string;
243 
244         Status toReturn = sfTcpSocket_send(sfPtr, data.ptr, data.length);
245         err.write(dsfml.system..string.toString(sfErr_getOutput()));
246         return toReturn;
247     }
248 
249     /**
250      * Send a formatted packet of data to the remote peer.
251      *
252      * This function will fail if the socket is not connected.
253      *
254      * Params:
255      * 	packet = Packet to send
256      *
257      * Returns: Status code.
258      */
259     Status send(Packet packet)
260     {
261         import std.stdio;
262         //temporary packet to be removed on function exit
263         scope SfPacket temp = new SfPacket();
264 
265         //getting packet's "to send" data
266         temp.append(packet.onSend());
267 
268         //send the data
269         return sfTcpSocket_sendPacket(sfPtr, temp.sfPtr);
270     }
271 
272     /**
273      * Receive raw data from the remote peer.
274      *
275      * In blocking mode, this function will wait until some bytes are actually
276      * received. This function will fail if the socket is not connected.
277      *
278      * Params:
279      * 	data = Array to fill with the received bytes
280      * 	sizeReceived = This variable is filled with the actual number of bytes
281                        received
282      *
283      * Returns: Status code.
284      */
285     Status receive(void[] data , out size_t sizeReceived)
286     {
287         return sfTcpSocket_receive(sfPtr, data.ptr, data.length, &sizeReceived);
288     }
289 
290     /**
291      * Receive a formatted packet of data from the remote peer.
292      *
293      * In blocking mode, this function will wait until the whole packet has been
294      * received. This function will fail if the socket is not connected.
295      *
296      * Params:
297      * 	packet = Packet to fill with the received data
298      *
299      * Returns: Status code.
300      */
301     Status receive(Packet packet)
302     {
303         //temporary packet to be removed on function exit
304         scope SfPacket temp = new SfPacket();
305 
306         //get the sent data
307         Status status = sfTcpSocket_receivePacket(sfPtr, temp.sfPtr);
308 
309         //put data into the packet so that it can process it first if it wants.
310         packet.onRecieve(temp.getData());
311 
312         return status;
313     }
314 }
315 
316 unittest
317 {
318     //TODO: Expand to use more methods in TcpSocket
319     version(DSFML_Unittest_Network)
320     {
321         import std.stdio;
322         import dsfml.network.tcplistener;
323 
324         writeln("Unittest for Tcp Socket");
325 
326         //socket connecting to server
327         auto clientSocket = new TcpSocket();
328 
329         //listener looking for new sockets
330         auto listener = new TcpListener();
331         listener.listen(55003);
332 
333         //get our client socket to connect to the server
334         clientSocket.connect(IpAddress.LocalHost, 55003);
335 
336         //packet to send data
337         auto sendPacket = new Packet();
338 
339         //Packet to receive data
340         auto receivePacket = new Packet();
341 
342         //socket on the server side connected to the client's socket
343         auto serverSocket = new TcpSocket();
344 
345         //accepts a new connection and binds it to the socket in the parameter
346         listener.accept(serverSocket);
347 
348         string temp = "I'm sending you stuff!";
349 
350         //Let's greet the server!
351         //sendPacket.writeString("Hello, I'm a client!");
352         //clientSocket.send(sendPacket);
353 
354         clientSocket.send(temp);
355 
356         //And get the data on the server side
357         //serverSocket.receive(receivePacket);
358 
359         char[1024] temp2;
360         size_t received;
361 
362         serverSocket.receive(temp2, received);
363 
364         //What did we get from the client?
365         writeln("Gotten from client: ", cast(string)temp2[0..received]);
366 
367         //clear the packets to send/get new information
368         sendPacket.clear();
369         receivePacket.clear();
370 
371         //Respond back to the client
372         sendPacket.write("Hello, I'm your server.");
373 
374         serverSocket.send(sendPacket);
375 
376         clientSocket.receive(receivePacket);
377 
378         string message;
379         receivePacket.read!string(message);
380         writeln("Gotten from server: ", message);
381 
382         clientSocket.disconnect();
383         writeln();
384     }
385 }
386 
387 package extern(C):
388 
389 struct sfTcpSocket;
390 
391 //Create a new TCP socket
392 sfTcpSocket* sfTcpSocket_create();
393 
394 
395 //Destroy a TCP socket
396 void sfTcpSocket_destroy(sfTcpSocket* socket);
397 
398 
399 //Set the blocking state of a TCP listener
400 void sfTcpSocket_setBlocking(sfTcpSocket* socket, bool blocking);
401 
402 
403 //Tell whether a TCP socket is in blocking or non-blocking mode
404 bool sfTcpSocket_isBlocking(const(sfTcpSocket)* socket);
405 
406 
407 //Get the port to which a TCP socket is bound locally
408 ushort sfTcpSocket_getLocalPort(const(sfTcpSocket)* socket);
409 
410 
411 //Get the address of the connected peer of a TCP socket
412 void sfTcpSocket_getRemoteAddress(const(sfTcpSocket)* socket, IpAddress* ipAddress);
413 
414 
415 //Get the port of the connected peer to which a TCP socket is connected
416 ushort sfTcpSocket_getRemotePort(const(sfTcpSocket)* socket);
417 
418 
419 //Connect a TCP socket to a remote peer
420 Socket.Status sfTcpSocket_connect(sfTcpSocket* socket, IpAddress* host, ushort port, long timeout);
421 
422 
423 //Disconnect a TCP socket from its remote peer
424 void sfTcpSocket_disconnect(sfTcpSocket* socket);
425 
426 
427 //Send raw data to the remote peer of a TCP socket
428 Socket.Status sfTcpSocket_send(sfTcpSocket* socket, const void* data, size_t size);
429 
430 
431 //Receive raw data from the remote peer of a TCP socket
432 Socket.Status sfTcpSocket_receive(sfTcpSocket* socket, void* data, size_t maxSize, size_t* sizeReceived);
433 
434 
435 //Send a formatted packet of data to the remote peer of a TCP socket
436 Socket.Status sfTcpSocket_sendPacket(sfTcpSocket* socket, sfPacket* packet);
437 
438 
439 //Receive a formatted packet of data from the remote peer
440 Socket.Status sfTcpSocket_receivePacket(sfTcpSocket* socket, sfPacket* packet);
441 
442 const(char)* sfErr_getOutput();