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);