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 UDP socket is a connectionless socket.
30  *
31  * Instead of connecting once to a remote host, like TCP sockets, it can send to
32  * and receive from any host at any time.
33  *
34  * It is a datagram protocol: bounded blocks of data (datagrams) are transfered
35  * over the network rather than a continuous stream of data (TCP). Therefore,
36  * one call to send will always match one call to receive (if the datagram is
37  * not lost), with the same data that was sent.
38  *
39  * The UDP protocol is lightweight but unreliable. Unreliable means that
40  * datagrams may be duplicated, be lost or arrive reordered. However, if a
41  * datagram arrives, its data is guaranteed to be valid.
42  *
43  * UDP is generally used for real-time communication (audio or video streaming,
44  * real-time games, etc.) where speed is crucial and lost data doesn't matter
45  * much.
46  *
47  * Sending and receiving data can use either the low-level or the high-level
48  * functions. The low-level functions process a raw sequence of bytes, whereas
49  * the high-level interface uses packets (see Packet), which are easier to use
50  * and provide more safety regarding the data that is exchanged. You can look at
51  * the Packet class to get more details about how they work.
52  *
53  * It is important to note that UdpSocket is unable to send datagrams bigger
54  * than MaxDatagramSize. In this case, it returns an error and doesn't send
55  * anything. This applies to both raw data and packets. Indeed, even packets are
56  * unable to split and recompose data, due to the unreliability of the protocol
57  * (dropped, mixed or duplicated datagrams may lead to a big mess when trying to
58  * recompose a packet).
59  *
60  * If the socket is bound to a port, it is automatically unbound from it when
61  * the socket is destroyed. However, you can unbind the socket explicitely with
62  * the Unbind function if necessary, to stop receiving messages or make the port
63  * available for other sockets.
64  *
65  * Example:
66  * ---
67  * // ----- The client -----
68  *
69  * // Create a socket and bind it to the port 55001
70  * auto socket = UdpSocket();
71  * socket.bind(55001);
72  *
73  * // Send a message to 192.168.1.50 on port 55002
74  * string message = "Hi, I am " ~ IpAddress.getLocalAddress().toString();
75  * socket.send(message, "192.168.1.50", 55002);
76  *
77  * // Receive an answer (most likely from 192.168.1.50, but could be anyone else)
78  * char[1024] buffer;
79  * size_t received = 0;
80  * IpAddress sender;
81  * ushort port;
82  * socket.receive(buffer, received, sender, port);
83  * writeln( sender.toString(), " said: ", buffer[0 .. received]);
84  *
85  * // ----- The server -----
86  *
87  * // Create a socket and bind it to the port 55002
88  * auto socket = new UdpSocket();
89  * socket.bind(55002);
90  *
91  * // Receive a message from anyone
92  * char[1024] buffer;
93  * size_t received = 0;
94  * IpAddress sender;
95  * ushort port;
96  * socket.receive(buffer, received, sender, port);
97  * writeln(sender.toString(), " said: ", buffer[0 .. received]);
98  *
99  * // Send an answer
100  * message = "Welcome " ~ sender.toString();
101  * socket.send(message, sender, port);
102  * ---
103  *
104  * See_Also:
105  * $(SOCKET_LINK), $(TCPSOCKET_LINK), $(PACKET_LINK)
106  */
107 module nudsfml.network.udpsocket;
108 
109 import nudsfml.network.ipaddress;
110 import nudsfml.network.packet;
111 import nudsfml.network.socket;
112 
113 import nudsfml.system.err;
114 
115 /**
116  * Specialized socket using the UDP protocol.
117  */
118 class UdpSocket:Socket
119 {
120     package sfUdpSocket* sfPtr;
121 
122     /// The maximum number of bytes that can be sent in a single UDP datagram.
123     enum maxDatagramSize = 65507;
124 
125     /// Default constructor.
126     this()
127     {
128         sfPtr = sfUdpSocket_create();
129     }
130 
131     /// Destructor.
132     ~this()
133     {
134         import nudsfml.system.config;
135         mixin(destructorOutput);
136         sfUdpSocket_destroy(sfPtr);
137     }
138 
139     /**
140      * Get the port to which the socket is bound locally.
141      *
142      * If the socket is not bound to a port, this function returns 0.
143      *
144      * Returns: Port to which the socket is bound.
145      */
146     ushort getLocalPort() const
147     {
148         return sfUdpSocket_getLocalPort(sfPtr);
149     }
150 
151     /**
152      * Set the blocking state of the socket.
153      *
154      * In blocking mode, calls will not return until they have completed their
155      * task. For example, a call to Receive in blocking mode won't return until
156      * some data was actually received. In non-blocking mode, calls will always
157      * return immediately, using the return code to signal whether there was
158      * data available or not. By default, all sockets are blocking.
159      *
160      * Params:
161      * 	blocking = true to set the socket as blocking, false for non-blocking
162      */
163     void setBlocking(bool blocking)
164     {
165         sfUdpSocket_setBlocking(sfPtr,blocking);
166     }
167 
168     /**
169      * Bind the socket to a specific port.
170      *
171      * Binding the socket to a port is necessary for being able to receive data
172      * on that port. You can use the special value Socket.AnyPort to tell the
173      * system to automatically pick an available port, and then call
174      * getLocalPort to retrieve the chosen port.
175      *
176      * Params:
177      * 	port    = Port to bind the socket to
178      *  address = Address of the interface to bind to
179      *
180      * Returns: Status code.
181      */
182     Status bind(ushort port, IpAddress address = IpAddress.Any)
183     {
184         return sfUdpSocket_bind(sfPtr,port, &address);
185     }
186 
187     /**
188      * Tell whether the socket is in blocking or non-blocking mode.
189      *
190      * Returns: true if the socket is blocking, false otherwise.
191      */
192     bool isBlocking() const
193     {
194         return (sfUdpSocket_isBlocking(sfPtr));
195     }
196 
197     /**
198      * Send raw data to a remote peer.
199      *
200      * Make sure that the size is not greater than UdpSocket.MaxDatagramSize,
201      * otherwise this function will fail and no data will be sent.
202      *
203      * Params:
204      * 	data    = Pointer to the sequence of bytes to send
205      * 	address = Address of the receiver
206      * 	port    = Port of the receiver to send the data to
207      *
208      * Returns: Status code.
209      */
210     Status send(const(void)[] data, IpAddress address, ushort port)
211     {
212         Status toReturn = sfUdpSocket_send(sfPtr,data.ptr, data.length,&address,port);
213 
214         return toReturn;
215     }
216 
217     /**
218      * Send a formatted packet of data to a remote peer.
219      *
220      * Make sure that the packet size is not greater than
221      * UdpSocket.MaxDatagramSize, otherwise this function will fail and no data
222      * will be sent.
223      *
224      * Params:
225      * 	packet = Packet to send
226      * 	address = Address of the receiver
227      * 	port = Port of the receiver to send the data to
228      *
229      * Returns: Status code.
230      */
231     Status send(Packet packet, IpAddress address, ushort port)
232     {
233         //temporary packet to be removed on function exit
234         scope SfPacket temp = new SfPacket();
235 
236         //getting packet's "to send" data
237         temp.append(packet.onSend());
238 
239         //send the data
240         return sfUdpSocket_sendPacket(sfPtr, temp.sfPtr, &address, port);
241     }
242 
243     /**
244      * Receive raw data from a remote peer.
245      *
246      * In blocking mode, this function will wait until some bytes are actually
247      * received.
248      *
249      * Be careful to use a buffer which is large enough for the data that you
250      * intend to receive, if it is too small then an error will be returned and
251      * all the data will be lost.
252      *
253      * Params:
254      * 		data = The array to fill with the received bytes
255      * 		sizeReceived = The number of bytes received
256      * 		address = Address of the peer that sent the data
257      * 		port = Port of the peer that sent the data
258      *
259      * Returns: Status code.
260      */
261     Status receive(void[] data, out size_t sizeReceived, out IpAddress address, out ushort port)
262     {
263         return sfUdpSocket_receive(sfPtr, data.ptr, data.length,
264                                             &sizeReceived, &address, &port);
265     }
266 
267     /**
268      * Receive a formatted packet of data from a remote peer.
269      *
270      * In blocking mode, this function will wait until the whole packet has been
271      * received.
272      *
273      * Params:
274      * 	packet = Packet to fill with the received data
275      * 	address = Address of the peer that sent the data
276      * 	port = Port of the peer that sent the data
277      *
278      * Returns: Status code.
279      */
280     Status receive(Packet packet, out IpAddress address, out ushort port)
281     {
282         //temporary packet to be removed on function exit
283         scope SfPacket temp = new SfPacket();
284 
285         //get the sent data
286         Status status =  sfUdpSocket_receivePacket(sfPtr, temp.sfPtr, &address, &port);
287 
288         //put data into the packet so that it can process it first if it wants.
289         packet.onRecieve(temp.getData());
290 
291         return status;
292     }
293 
294     /**
295      * Unbind the socket from the local port to which it is bound.
296      *
297      * The port that the socket was previously using is immediately available
298      * after this function is called. If the socket is not bound to a port, this
299      * function has no effect.
300      */
301     void unbind()
302     {
303         sfUdpSocket_unbind(sfPtr);
304     }
305 }
306 
307 unittest
308 {
309     version(DSFML_Unittest_Network)
310     {
311         import std.stdio;
312 
313         writeln("Unittest for Udp Socket");
314 
315         auto clientSocket = new UdpSocket();
316 
317         //bind this socket to this port for receiving data
318         clientSocket.bind(56001);
319 
320         auto serverSocket = new UdpSocket();
321 
322         serverSocket.bind(56002);
323 
324         //send the data to the port our server is listening to
325         writeln("Sending data!");
326         clientSocket.send("I sent you data!", IpAddress.LocalHost, 56002);
327 
328         ////////////////////////////////////////////////////////////////////////
329 
330         IpAddress receivedFrom;
331         ushort receivedPort;
332         auto receivedPacket = new Packet();
333 
334         //get the information received as well as information about the sender
335 
336         char[1024] temp2;
337         size_t received;
338 
339         writeln("Receiving data!");
340         serverSocket.receive(temp2,received, receivedFrom, receivedPort);
341 
342         //What did we get?!
343         writeln("The data received from ", receivedFrom, " at port ", receivedPort, " was: ", cast(string)temp2[0..received]);
344 
345         writeln();
346     }
347 }
348 
349 package extern(C):
350 
351 struct sfUdpSocket;
352 
353 //Create a new UDP socket
354 sfUdpSocket* sfUdpSocket_create();
355 
356 //Destroy a UDP socket
357 void sfUdpSocket_destroy(sfUdpSocket* socket);
358 
359 //Set the blocking state of a UDP listener
360 void sfUdpSocket_setBlocking(sfUdpSocket* socket, bool blocking);
361 
362 //Tell whether a UDP socket is in blocking or non-blocking mode
363 bool sfUdpSocket_isBlocking(const sfUdpSocket* socket);
364 
365 //Get the port to which a UDP socket is bound locally
366 ushort sfUdpSocket_getLocalPort(const(sfUdpSocket)* socket);
367 
368 //Bind a UDP socket to a specific port
369 Socket.Status sfUdpSocket_bind(sfUdpSocket* socket, ushort port, IpAddress* ipAddress);
370 
371 //Unbind a UDP socket from the local port to which it is bound
372 void sfUdpSocket_unbind(sfUdpSocket* socket);
373 
374 //Send raw data to a remote peer with a UDP socket
375 Socket.Status sfUdpSocket_send(sfUdpSocket* socket, const(void)* data, size_t size, IpAddress* receiver, ushort port);
376 
377 //Receive raw data from a remote peer with a UDP socket
378 Socket.Status sfUdpSocket_receive(sfUdpSocket* socket, void* data, size_t maxSize, size_t* sizeReceived, IpAddress* sender, ushort* port);
379 
380 //Send a formatted packet of data to a remote peer with a UDP socket
381 Socket.Status sfUdpSocket_sendPacket(sfUdpSocket* socket, sfPacket* packet, IpAddress* receiver, ushort port);
382 
383 //Receive a formatted packet of data from a remote peer with a UDP socket
384 Socket.Status sfUdpSocket_receivePacket(sfUdpSocket* socket, sfPacket* packet, IpAddress* sender, ushort* port);