SocketUDP.cpp
1 //
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 
36 namespace sf
37 {
42 {
43  Create();
44 }
45 
46 
50 void 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 
64 bool 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 
118 Socket::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 
162 Socket::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 
219 Socket::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 
245 Socket::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 
340 bool SocketUDP::IsValid() const
341 {
342  return mySocket != SocketHelper::InvalidSocket();
343 }
344 
345 
349 unsigned short SocketUDP::GetPort() const
350 {
351  return myPort;
352 }
353 
354 
358 bool SocketUDP::operator ==(const SocketUDP& Other) const
359 {
360  return mySocket == Other.mySocket;
361 }
362 
363 
367 bool SocketUDP::operator !=(const SocketUDP& Other) const
368 {
369  return mySocket != Other.mySocket;
370 }
371 
372 
378 bool SocketUDP::operator <(const SocketUDP& Other) const
379 {
380  return mySocket < Other.mySocket;
381 }
382 
383 
388 SocketUDP::SocketUDP(SocketHelper::SocketType Descriptor)
389 {
390  Create(Descriptor);
391 }
392 
393 
397 void 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
bool operator<(const SocketUDP &Other) const
Comparison operator <.
Definition: SocketUDP.cpp:378
bool Close()
Close the socket.
Definition: SocketUDP.cpp:316
static Socket::Status GetErrorStatus()
Get the last socket error status.
SocketUDP wraps a socket using UDP protocol to send data fastly (but with less safety) ...
Definition: SocketUDP.hpp:45
Packet wraps data to send / to receive through the network.
Definition: Packet.hpp:41
unsigned short GetPort() const
Get the port the socket is currently bound to.
Definition: SocketUDP.cpp:349
static SocketType InvalidSocket()
Return the value of the invalid socket.
bool IsValid() const
Check if the socket is in a valid state ; this function can be called any time to check if the socket...
Definition: SocketUDP.cpp:340
std::string ToString() const
Get a string representation of the address.
Definition: IPAddress.cpp:135
void Clear()
Clear the packet data.
Definition: Packet.cpp:72
bool operator==(const SocketUDP &Other) const
Comparison operator ==.
Definition: SocketUDP.cpp:358
bool Bind(unsigned short Port)
Bind the socket to a specific port.
Definition: SocketUDP.cpp:64
IPAddress provides easy manipulation of IP v4 addresses.
Definition: IPAddress.hpp:42
bool Unbind()
Unbind the socket from its previous port, if any.
Definition: SocketUDP.cpp:101
static void SetBlocking(SocketType Socket, bool Block)
Set a socket as blocking or non-blocking.
Socket::Status Receive(char *Data, std::size_t MaxSize, std::size_t &SizeReceived, IPAddress &Address, unsigned short &Port)
Receive an array of bytes.
Definition: SocketUDP.cpp:162
Socket::Status Send(const char *Data, std::size_t Size, const IPAddress &Address, unsigned short Port)
Send an array of bytes.
Definition: SocketUDP.cpp:118
void SetBlocking(bool Blocking)
Change the blocking state of the socket.
Definition: SocketUDP.cpp:50
static bool Close(SocketType Socket)
Close / destroy a socket.
SocketUDP()
Default constructor.
Definition: SocketUDP.cpp:41
bool operator!=(const SocketUDP &Other) const
Comparison operator !=.
Definition: SocketUDP.cpp:367