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