1 /* 2 DSFML - The Simple and Fast Multimedia Library for D 3 4 Copyright (c) 2013 - 2015 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 use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, including commercial applications, 10 and to alter it and redistribute it freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 14 15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 16 17 3. This notice may not be removed or altered from any source distribution 18 */ 19 20 ///A module containing the UdpSocket Class. 21 module dsfml.network.udpsocket; 22 23 import dsfml.network.packet; 24 import dsfml.network.ipaddress; 25 import dsfml.network.socket; 26 27 import dsfml.system.err; 28 29 /** 30 *Specialized socket using the UDP protocol. 31 * 32 *A UDP socket is a connectionless socket. 33 * 34 *Instead of connecting once to a remote host, like TCP sockets, it can send to and receive from any host at any time. 35 * 36 *It is a datagram protocol: bounded blocks of data (datagrams) are transfered over the network rather than a continuous stream of data (TCP). Therefore, one call to send will always match one call to receive (if the datagram is not lost), with the same data that was sent. 37 * 38 *The UDP protocol is lightweight but unreliable. Unreliable means that datagrams may be duplicated, be lost or arrive reordered. However, if a datagram arrives, its data is guaranteed to be valid. 39 * 40 *UDP is generally used for real-time communication (audio or video streaming, real-time games, etc.) where speed is crucial and lost data doesn't matter much. 41 * 42 *Sending and receiving data can use either the low-level or the high-level functions. The low-level functions process a raw sequence of bytes, whereas the high-level interface uses packets (see sf::Packet), which are easier to use and provide more safety regarding the data that is exchanged. You can look at the sf::Packet class to get more details about how they work. 43 * 44 *It is important to note that UdpSocket is unable to send datagrams bigger than MaxDatagramSize. In this case, it returns an error and doesn't send anything. This applies to both raw data and packets. Indeed, even packets are unable to split and recompose data, due to the unreliability of the protocol (dropped, mixed or duplicated datagrams may lead to a big mess when trying to recompose a packet). 45 * 46 *If the socket is bound to a port, it is automatically unbound from it when the socket is destroyed. However, you can unbind the socket explicitely with the Unbind function if necessary, to stop receiving messages or make the port available for other sockets. 47 */ 48 class UdpSocket:Socket 49 { 50 package sfUdpSocket* sfPtr; 51 52 ///The maximum number of bytes that can be sent in a single UDP datagram. 53 enum maxDatagramSize = 65507; 54 55 ///Default constructor 56 this() 57 { 58 sfPtr = sfUdpSocket_create(); 59 } 60 61 ///Destructor 62 ~this() 63 { 64 import dsfml.system.config; 65 mixin(destructorOutput); 66 sfUdpSocket_destroy(sfPtr); 67 } 68 69 ///Get the port to which the socket is bound locally. 70 /// 71 ///If the socket is not bound to a port, this function returns 0. 72 /// 73 ///Returns: Port to which the socket is bound. 74 ushort getLocalPort() 75 { 76 return sfUdpSocket_getLocalPort(sfPtr); 77 } 78 79 ///Set the blocking state of the socket. 80 /// 81 ///In blocking mode, calls will not return until they have completed their task. For example, a call to Receive in blocking mode won't return until some data was actually received. In non-blocking mode, calls will always return immediately, using the return code to signal whether there was data available or not. By default, all sockets are blocking. 82 /// 83 ///Params: 84 /// blocking = True to set the socket as blocking, false for non-blocking. 85 void setBlocking(bool blocking) 86 { 87 sfUdpSocket_setBlocking(sfPtr,blocking); 88 } 89 90 ///Bind the socket to a specific port. 91 /// 92 ///Binding the socket to a port is necessary for being able to receive data on that port. You can use the special value Socket::AnyPort to tell the system to automatically pick an available port, and then call getLocalPort to retrieve the chosen port. 93 /// 94 ///Params: 95 /// port = Port to bind the socket to. 96 /// 97 ///Returns: Status code. 98 Status bind(ushort port) 99 { 100 import dsfml.system.string; 101 102 Status toReturn = sfUdpSocket_bind(sfPtr,port); 103 err.write(dsfml.system..string.toString(sfErr_getOutput())); 104 return toReturn; 105 } 106 107 ///Tell whether the socket is in blocking or non-blocking mode. 108 /// 109 ///Returns: True if the socket is blocking, false otherwise. 110 bool isBlocking() 111 { 112 return (sfUdpSocket_isBlocking(sfPtr)); 113 } 114 115 ///Send raw data to a remote peer. 116 /// 117 ///Make sure that the size is not greater than UdpSocket.MaxDatagramSize, otherwise this function will fail and no data will be sent. 118 /// 119 ///Params: 120 /// data = Pointer to the sequence of bytes to send. 121 /// address = Address of the receiver. 122 /// port = Port of the receiver to send the data to. 123 /// 124 ///Returns: Status code. 125 Status send(const(void)[] data, IpAddress address, ushort port) 126 { 127 Status toReturn = sfUdpSocket_send(sfPtr,data.ptr, data.length,address.m_address.ptr,port); 128 129 return toReturn; 130 } 131 132 ///Send a formatted packet of data to a remote peer. 133 /// 134 ///Make sure that the packet size is not greater than UdpSocket.MaxDatagramSize, otherwise this function will fail and no data will be sent. 135 /// 136 ///Params: 137 /// packet = Packet to send. 138 /// address = Address of the receiver. 139 /// port = Port of the receiver to send the data to. 140 /// 141 ///Returns: Status code 142 Status send(Packet packet, IpAddress address, ushort port) 143 { 144 //temporary packet to be removed on function exit 145 scope SfPacket temp = new SfPacket(); 146 147 //getting packet's "to send" data 148 temp.append(packet.onSend()); 149 150 //send the data 151 return sfUdpSocket_sendPacket(sfPtr, temp.sfPtr,address.m_address.ptr,port); 152 } 153 154 ///Receive raw data from a remote peer. 155 /// 156 ///In blocking mode, this function will wait until some bytes are actually received. 157 ///Be careful to use a buffer which is large enough for the data that you intend to receive, if it is too small then an error will be returned and all the data will be lost. 158 /// 159 ///Params: 160 /// data = The array to fill with the received bytes 161 /// sizeReceived 162 /// address = Address of the peer that sent the data 163 /// port = Port of the peer that sent the data 164 /// 165 ///Returns: Status code. 166 Status receive(void[] data, out size_t sizeReceived, out IpAddress address, out ushort port) 167 { 168 import dsfml.system.string; 169 170 Status status; 171 172 void* temp = sfUdpSocket_receive(sfPtr, data.length, &sizeReceived, address.m_address.ptr, &port, &status); 173 174 err.write(dsfml.system..string.toString(sfErr_getOutput())); 175 176 data[0..sizeReceived] = temp[0..sizeReceived].dup; 177 178 return status; 179 } 180 181 ///Receive a formatted packet of data from a remote peer. 182 /// 183 ///In blocking mode, this function will wait until the whole packet has been received. 184 /// 185 ///Params: 186 /// packet = Packet to fill with the received data. 187 /// address = Address of the peer that sent the data. 188 /// port = Port of the peer that sent the data. 189 /// 190 ///Returns: Status code. 191 Status receive(Packet packet, out IpAddress address, out ushort port) 192 { 193 //temporary packet to be removed on function exit 194 scope SfPacket temp = new SfPacket(); 195 196 //get the sent data 197 Status status = sfUdpSocket_receivePacket(sfPtr, temp.sfPtr, address.m_address.ptr, &port); 198 199 //put data into the packet so that it can process it first if it wants. 200 packet.onRecieve(temp.getData()); 201 202 return status; 203 } 204 205 ///Unbind the socket from the local port to which it is bound. 206 /// 207 ///The port that the socket was previously using is immediately available after this function is called. If the socket is not bound to a port, this function has no effect. 208 void unbind() 209 { 210 sfUdpSocket_unbind(sfPtr); 211 } 212 213 } 214 215 unittest 216 { 217 version(DSFML_Unittest_Network) 218 { 219 import std.stdio; 220 221 writeln("Unittest for Udp Socket"); 222 223 auto clientSocket = new UdpSocket(); 224 225 //bind this socket to this port for receiving data 226 clientSocket.bind(56001); 227 228 auto serverSocket = new UdpSocket(); 229 230 serverSocket.bind(56002); 231 232 233 //auto sendingPacket = new Packet(); 234 235 //sendingPacket.writeString("I sent you data!"); 236 writeln("Sending data!"); 237 //send the data to the port our server is listening to 238 clientSocket.send("I sent you data!", IpAddress.LocalHost, 56002); 239 240 241 IpAddress receivedFrom; 242 ushort receivedPort; 243 auto receivedPacket = new Packet(); 244 245 //get the information received as well as information about the sender 246 //serverSocket.receive(receivedPacket,receivedFrom, receivedPort); 247 248 char[1024] temp2; 249 size_t received; 250 251 writeln("Receiving data!"); 252 serverSocket.receive(temp2,received, receivedFrom, receivedPort); 253 254 //What did we get?! 255 writeln("The data received from ", receivedFrom.toString(), " at port ", receivedPort, " was: ", cast(string)temp2[0..received]); 256 257 258 writeln(); 259 } 260 } 261 262 package extern(C): 263 264 struct sfUdpSocket; 265 266 //Create a new UDP socket 267 sfUdpSocket* sfUdpSocket_create(); 268 269 //Destroy a UDP socket 270 void sfUdpSocket_destroy(sfUdpSocket* socket); 271 272 //Set the blocking state of a UDP listener 273 void sfUdpSocket_setBlocking(sfUdpSocket* socket, bool blocking); 274 275 //Tell whether a UDP socket is in blocking or non-blocking mode 276 bool sfUdpSocket_isBlocking(const sfUdpSocket* socket); 277 278 //Get the port to which a UDP socket is bound locally 279 ushort sfUdpSocket_getLocalPort(const(sfUdpSocket)* socket); 280 281 //Bind a UDP socket to a specific port 282 Socket.Status sfUdpSocket_bind(sfUdpSocket* socket, ushort port); 283 284 //Unbind a UDP socket from the local port to which it is bound 285 void sfUdpSocket_unbind(sfUdpSocket* socket); 286 287 //Send raw data to a remote peer with a UDP socket 288 Socket.Status sfUdpSocket_send(sfUdpSocket* socket, const(void)* data, size_t size, const(char)* ipAddress, ushort port); 289 290 //Receive raw data from a remote peer with a UDP socket 291 void* sfUdpSocket_receive(sfUdpSocket* socket, size_t maxSize, size_t* sizeReceived, char* ipAddress, ushort* port, Socket.Status* status); 292 293 //Send a formatted packet of data to a remote peer with a UDP socket 294 Socket.Status sfUdpSocket_sendPacket(sfUdpSocket* socket, sfPacket* packet, const(char)* ipAddress, ushort port); 295 296 //Receive a formatted packet of data from a remote peer with a UDP socket 297 Socket.Status sfUdpSocket_receivePacket(sfUdpSocket* socket, sfPacket* packet, char* address, ushort* port); 298 299 const(char)* sfErr_getOutput();