SocketUDP.cpp
1
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@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,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
26// Headers
28#include <SFML/Network/SocketUDP.hpp>
29#include <SFML/Network/IPAddress.hpp>
30#include <SFML/Network/Packet.hpp>
31#include <algorithm>
32#include <iostream>
33#include <string.h>
34
35
36namespace sf
37{
42{
43 Create();
44}
45
46
50void SocketUDP::SetBlocking(bool Blocking)
51{
52 // Make sure our socket is valid
53 if (!IsValid())
54 Create();
55
56 SocketHelper::SetBlocking(mySocket, Blocking);
57 myIsBlocking = Blocking;
58}
59
60
64bool SocketUDP::Bind(unsigned short Port)
65{
66 // Check if the socket is already bound to the specified port
67 if (myPort != Port)
68 {
69 // If the socket was previously bound to another port, we need to unbind it first
70 Unbind();
71
72 if (Port != 0)
73 {
74 // Build an address with the specified port
75 sockaddr_in Addr;
76 Addr.sin_family = AF_INET;
77 Addr.sin_port = htons(Port);
78 Addr.sin_addr.s_addr = INADDR_ANY;
79 memset(Addr.sin_zero, 0, sizeof(Addr.sin_zero));
80
81 // Bind the socket to the port
82 if (bind(mySocket, reinterpret_cast<sockaddr*>(&Addr), sizeof(Addr)) == -1)
83 {
84 std::cerr << "Failed to bind the socket to port " << Port << std::endl;
85 myPort = 0;
86 return false;
87 }
88 }
89
90 // Save the new port
91 myPort = Port;
92 }
93
94 return true;
95}
96
97
102{
103 // To unbind the socket, we just recreate it
104 if (myPort != 0)
105 {
106 Close();
107 Create();
108 myPort = 0;
109 }
110
111 return true;
112}
113
114
118Socket::Status SocketUDP::Send(const char* Data, std::size_t Size, const IPAddress& Address, unsigned short Port)
119{
120 // Make sure the socket is valid
121 if (!IsValid())
122 Create();
123
124 // Check parameters
125 if (Data && Size)
126 {
127 // Build the target address
128 sockaddr_in Target;
129 Target.sin_family = AF_INET;
130 Target.sin_port = htons(Port);
131 Target.sin_addr.s_addr = inet_addr(Address.ToString().c_str());
132 memset(Target.sin_zero, 0, sizeof(Target.sin_zero));
133
134 // Loop until every byte has been sent
135 int Sent = 0;
136 int SizeToSend = static_cast<int>(Size);
137 for (int Length = 0; Length < SizeToSend; Length += Sent)
138 {
139 // Send a chunk of data
140 Sent = sendto(mySocket, Data + Length, SizeToSend - Length, 0, reinterpret_cast<sockaddr*>(&Target), sizeof(Target));
141
142 // Check errors
143 if (Sent <= 0)
145 }
146
147 return Socket::Done;
148 }
149 else
150 {
151 // Error...
152 std::cerr << "Cannot send data over the network (invalid parameters)" << std::endl;
153 return Socket::Error;
154 }
155}
156
157
162Socket::Status SocketUDP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived, IPAddress& Address, unsigned short& Port)
163{
164 // First clear the size received
165 SizeReceived = 0;
166
167 // Make sure the socket is bound to a port
168 if (myPort == 0)
169 {
170 std::cerr << "Failed to receive data ; the UDP socket first needs to be bound to a port" << std::endl;
171 return Socket::Error;
172 }
173
174 // Make sure the socket is valid
175 if (!IsValid())
176 Create();
177
178 // Check parameters
179 if (Data && MaxSize)
180 {
181 // Data that will be filled with the other computer's address
182 sockaddr_in Sender;
183 Sender.sin_family = AF_INET;
184 Sender.sin_port = 0;
185 Sender.sin_addr.s_addr = INADDR_ANY;
186 memset(Sender.sin_zero, 0, sizeof(Sender.sin_zero));
187 SocketHelper::LengthType SenderSize = sizeof(Sender);
188
189 // Receive a chunk of bytes
190 int Received = recvfrom(mySocket, Data, static_cast<int>(MaxSize), 0, reinterpret_cast<sockaddr*>(&Sender), &SenderSize);
191
192 // Check the number of bytes received
193 if (Received > 0)
194 {
195 Address = IPAddress(inet_ntoa(Sender.sin_addr));
196 Port = ntohs(Sender.sin_port);
197 SizeReceived = static_cast<std::size_t>(Received);
198 return Socket::Done;
199 }
200 else
201 {
202 Address = IPAddress();
203 Port = 0;
204 return Received == 0 ? Socket::Disconnected : SocketHelper::GetErrorStatus();
205 }
206 }
207 else
208 {
209 // Error...
210 std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
211 return Socket::Error;
212 }
213}
214
215
219Socket::Status SocketUDP::Send(Packet& PacketToSend, const IPAddress& Address, unsigned short Port)
220{
221 // Get the data to send from the packet
222 std::size_t DataSize = 0;
223 const char* Data = PacketToSend.OnSend(DataSize);
224
225 // Send the packet size
226 Uint32 PacketSize = htonl(static_cast<unsigned long>(DataSize));
227 Send(reinterpret_cast<const char*>(&PacketSize), sizeof(PacketSize), Address, Port);
228
229 // Send the packet data
230 if (PacketSize > 0)
231 {
232 return Send(Data, DataSize, Address, Port);
233 }
234 else
235 {
236 return Socket::Done;
237 }
238}
239
240
245Socket::Status SocketUDP::Receive(Packet& PacketToReceive, IPAddress& Address, unsigned short& Port)
246{
247 // We start by getting the size of the incoming packet
248 Uint32 PacketSize = 0;
249 std::size_t Received = 0;
250 if (myPendingPacketSize < 0)
251 {
252 // Loop until we've received the entire size of the packet
253 // (even a 4 bytes variable may be received in more than one call)
254 while (myPendingHeaderSize < sizeof(myPendingHeader))
255 {
256 char* Data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
257 Socket::Status Status = Receive(Data, sizeof(myPendingHeader) - myPendingHeaderSize, Received, Address, Port);
258 myPendingHeaderSize += Received;
259
260 if (Status != Socket::Done)
261 return Status;
262 }
263
264 PacketSize = ntohl(myPendingHeader);
265 myPendingHeaderSize = 0;
266 }
267 else
268 {
269 // There is a pending packet : we already know its size
270 PacketSize = myPendingPacketSize;
271 }
272
273 // Use another address instance for receiving the packet data ;
274 // chunks of data coming from a different sender will be discarded (and lost...)
275 IPAddress Sender;
276 unsigned short SenderPort;
277
278 // Then loop until we receive all the packet data
279 char Buffer[1024];
280 while (myPendingPacket.size() < PacketSize)
281 {
282 // Receive a chunk of data
283 std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
284 Socket::Status Status = Receive(Buffer, SizeToGet, Received, Sender, SenderPort);
285 if (Status != Socket::Done)
286 {
287 // We must save the size of the pending packet until we can receive its content
288 if (Status == Socket::NotReady)
289 myPendingPacketSize = PacketSize;
290 return Status;
291 }
292
293 // Append it into the packet
294 if ((Sender == Address) && (SenderPort == Port) && (Received > 0))
295 {
296 myPendingPacket.resize(myPendingPacket.size() + Received);
297 char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
298 memcpy(Begin, Buffer, Received);
299 }
300 }
301
302 // We have received all the datas : we can copy it to the user packet, and clear our internal packet
303 PacketToReceive.Clear();
304 if (!myPendingPacket.empty())
305 PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
306 myPendingPacket.clear();
307 myPendingPacketSize = -1;
308
309 return Socket::Done;
310}
311
312
317{
318 if (IsValid())
319 {
320 if (!SocketHelper::Close(mySocket))
321 {
322 std::cerr << "Failed to close socket" << std::endl;
323 return false;
324 }
325
326 mySocket = SocketHelper::InvalidSocket();
327 }
328
329 myPort = 0;
330 myIsBlocking = true;
331
332 return true;
333}
334
335
341{
342 return mySocket != SocketHelper::InvalidSocket();
343}
344
345
349unsigned short SocketUDP::GetPort() const
350{
351 return myPort;
352}
353
354
358bool SocketUDP::operator ==(const SocketUDP& Other) const
359{
360 return mySocket == Other.mySocket;
361}
362
363
367bool SocketUDP::operator !=(const SocketUDP& Other) const
368{
369 return mySocket != Other.mySocket;
370}
371
372
378bool SocketUDP::operator <(const SocketUDP& Other) const
379{
380 return mySocket < Other.mySocket;
381}
382
383
388SocketUDP::SocketUDP(SocketHelper::SocketType Descriptor)
389{
390 Create(Descriptor);
391}
392
393
397void SocketUDP::Create(SocketHelper::SocketType Descriptor)
398{
399 // Use the given socket descriptor, or get a new one
400 mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_DGRAM, 0);
401 myIsBlocking = true;
402
403 // Clear the last port used
404 myPort = 0;
405
406 // Reset the pending packet
407 myPendingHeaderSize = 0;
408 myPendingPacket.clear();
409 myPendingPacketSize = -1;
410
411 // Setup default options
412 if (IsValid())
413 {
414 // To avoid the "Address already in use" error message when trying to bind to the same port
415 int Yes = 1;
416 if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
417 {
418 std::cerr << "Failed to set socket option \"reuse address\" ; "
419 << "binding to a same port may fail if too fast" << std::endl;
420 }
421
422 // Enable broadcast by default
423 if (setsockopt(mySocket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
424 {
425 std::cerr << "Failed to enable broadcast on UDP socket" << std::endl;
426 }
427
428 // Set blocking by default (should always be the case anyway)
429 SetBlocking(true);
430 }
431}
432
433} // namespace sf
IPAddress provides easy manipulation of IP v4 addresses.
Definition IPAddress.hpp:43
std::string ToString() const
Get a string representation of the address.
Packet wraps data to send / to receive through the network.
Definition Packet.hpp:42
void Clear()
Clear the packet data.
Definition Packet.cpp:72
static void SetBlocking(SocketType Socket, bool Block)
Set a socket as blocking or non-blocking.
static Socket::Status GetErrorStatus()
Get the last socket error status.
static SocketType InvalidSocket()
Return the value of the invalid socket.
static bool Close(SocketType Socket)
Close / destroy a socket.
unsigned short GetPort() const
Get the port the socket is currently bound to.
void SetBlocking(bool Blocking)
Change the blocking state of the socket.
Definition SocketUDP.cpp:50
bool Bind(unsigned short Port)
Bind the socket to a specific port.
Definition SocketUDP.cpp:64
bool Unbind()
Unbind the socket from its previous port, if any.
bool operator==(const SocketUDP &Other) const
Comparison operator ==.
SocketUDP()
Default constructor.
Definition SocketUDP.cpp:41
bool IsValid() const
Check if the socket is in a valid state ; this function can be called any time to check if the socket...
Socket::Status Send(const char *Data, std::size_t Size, const IPAddress &Address, unsigned short Port)
Send an array of bytes.
Socket::Status Receive(char *Data, std::size_t MaxSize, std::size_t &SizeReceived, IPAddress &Address, unsigned short &Port)
Receive an array of bytes.
bool Close()
Close the socket.
bool operator<(const SocketUDP &Other) const
Comparison operator <.
bool operator!=(const SocketUDP &Other) const
Comparison operator !=.