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