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