SocketTCP.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/SocketTCP.hpp>
29 #include <SFML/Network/IPAddress.hpp>
30 #include <SFML/Network/Packet.hpp>
31 #include <SFML/Network/SocketHelper.hpp>
32 #include <algorithm>
33 #include <iostream>
34 #include <string.h>
35 
36 
37 #ifdef _MSC_VER
38  #pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
39 #endif
40 
41 
42 namespace sf
43 {
48 {
50 }
51 
52 
56 void SocketTCP::SetBlocking(bool Blocking)
57 {
58  // Make sure our socket is valid
59  if (!IsValid())
60  Create();
61 
62  SocketHelper::SetBlocking(mySocket, Blocking);
63  myIsBlocking = Blocking;
64 }
65 
66 
70 Socket::Status SocketTCP::Connect(unsigned short Port, const IPAddress& HostAddress, float Timeout)
71 {
72  // Make sure our socket is valid
73  if (!IsValid())
74  Create();
75 
76  // Build the host address
77  sockaddr_in SockAddr;
78  memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
79  SockAddr.sin_addr.s_addr = inet_addr(HostAddress.ToString().c_str());
80  SockAddr.sin_family = AF_INET;
81  SockAddr.sin_port = htons(Port);
82 
83  if (Timeout <= 0)
84  {
85  // ----- We're not using a timeout : just try to connect -----
86 
87  if (connect(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
88  {
89  // Failed to connect
91  }
92 
93  // Connection succeeded
94  return Socket::Done;
95  }
96  else
97  {
98  // ----- We're using a timeout : we'll need a few tricks to make it work -----
99 
100  // Save the previous blocking state
101  bool IsBlocking = myIsBlocking;
102 
103  // Switch to non-blocking to enable our connection timeout
104  if (IsBlocking)
105  SetBlocking(false);
106 
107  // Try to connect to host
108  if (connect(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) >= 0)
109  {
110  // We got instantly connected! (it may no happen a lot...)
111  return Socket::Done;
112  }
113 
114  // Get the error status
115  Socket::Status Status = SocketHelper::GetErrorStatus();
116 
117  // If we were in non-blocking mode, return immediatly
118  if (!IsBlocking)
119  return Status;
120 
121  // Otherwise, wait until something happens to our socket (success, timeout or error)
122  if (Status == Socket::NotReady)
123  {
124  // Setup the selector
125  fd_set Selector;
126  FD_ZERO(&Selector);
127  FD_SET(mySocket, &Selector);
128 
129  // Setup the timeout
130  timeval Time;
131  Time.tv_sec = static_cast<long>(Timeout);
132  Time.tv_usec = (static_cast<long>(Timeout * 1000) % 1000) * 1000;
133 
134  // Wait for something to write on our socket (which means that the connection request has returned)
135  if (select(static_cast<int>(mySocket + 1), NULL, &Selector, NULL, &Time) > 0)
136  {
137  // At this point the connection may have been either accepted or refused.
138  // To know whether it's a success or a failure, we try to retrieve the name of the connected peer
139  SocketHelper::LengthType Size = sizeof(SockAddr);
140  if (getpeername(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), &Size) != -1)
141  {
142  // Connection accepted
143  Status = Socket::Done;
144  }
145  else
146  {
147  // Connection failed
148  Status = SocketHelper::GetErrorStatus();
149  }
150  }
151  else
152  {
153  // Failed to connect before timeout is over
154  Status = SocketHelper::GetErrorStatus();
155  }
156  }
157 
158  // Switch back to blocking mode
159  SetBlocking(true);
160 
161  return Status;
162  }
163 }
164 
165 
169 bool SocketTCP::Listen(unsigned short Port)
170 {
171  // Make sure our socket is valid
172  if (!IsValid())
173  Create();
174 
175  // Build the address
176  sockaddr_in SockAddr;
177  memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
178  SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
179  SockAddr.sin_family = AF_INET;
180  SockAddr.sin_port = htons(Port);
181 
182  // Bind the socket to the specified port
183  if (bind(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
184  {
185  // Not likely to happen, but...
186  std::cerr << "Failed to bind socket to port " << Port << std::endl;
187  return false;
188  }
189 
190  // Listen to the bound port
191  if (listen(mySocket, 0) == -1)
192  {
193  // Oops, socket is deaf
194  std::cerr << "Failed to listen to port " << Port << std::endl;
195  return false;
196  }
197 
198  return true;
199 }
200 
201 
206 Socket::Status SocketTCP::Accept(SocketTCP& Connected, IPAddress* Address)
207 {
208  // Address that will be filled with client informations
209  sockaddr_in ClientAddress;
210  SocketHelper::LengthType Length = sizeof(ClientAddress);
211 
212  // Accept a new connection
213  Connected = accept(mySocket, reinterpret_cast<sockaddr*>(&ClientAddress), &Length);
214 
215  // Check errors
216  if (!Connected.IsValid())
217  {
218  if (Address)
219  *Address = IPAddress();
220 
222  }
223 
224  // Fill address if requested
225  if (Address)
226  *Address = IPAddress(inet_ntoa(ClientAddress.sin_addr));
227 
228  return Socket::Done;
229 }
230 
231 
235 Socket::Status SocketTCP::Send(const char* Data, std::size_t Size)
236 {
237  // First check that socket is valid
238  if (!IsValid())
239  return Socket::Error;
240 
241  // Check parameters
242  if (Data && Size)
243  {
244  // Loop until every byte has been sent
245  int Sent = 0;
246  int SizeToSend = static_cast<int>(Size);
247  for (int Length = 0; Length < SizeToSend; Length += Sent)
248  {
249  // Send a chunk of data
250  Sent = send(mySocket, Data + Length, SizeToSend - Length, 0);
251 
252  // Check if an error occured
253  if (Sent <= 0)
255  }
256 
257  return Socket::Done;
258  }
259  else
260  {
261  // Error...
262  std::cerr << "Cannot send data over the network (invalid parameters)" << std::endl;
263  return Socket::Error;
264  }
265 }
266 
267 
272 Socket::Status SocketTCP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived)
273 {
274  // First clear the size received
275  SizeReceived = 0;
276 
277  // Check that socket is valid
278  if (!IsValid())
279  return Socket::Error;
280 
281  // Check parameters
282  if (Data && MaxSize)
283  {
284  // Receive a chunk of bytes
285  int Received = recv(mySocket, Data, static_cast<int>(MaxSize), 0);
286 
287  // Check the number of bytes received
288  if (Received > 0)
289  {
290  SizeReceived = static_cast<std::size_t>(Received);
291  return Socket::Done;
292  }
293  else if (Received == 0)
294  {
295  return Socket::Disconnected;
296  }
297  else
298  {
300  }
301  }
302  else
303  {
304  // Error...
305  std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
306  return Socket::Error;
307  }
308 }
309 
310 
314 Socket::Status SocketTCP::Send(Packet& PacketToSend)
315 {
316  // Get the data to send from the packet
317  std::size_t DataSize = 0;
318  const char* Data = PacketToSend.OnSend(DataSize);
319 
320  // Send the packet size
321  Uint32 PacketSize = htonl(static_cast<unsigned long>(DataSize));
322  Send(reinterpret_cast<const char*>(&PacketSize), sizeof(PacketSize));
323 
324  // Send the packet data
325  if (PacketSize > 0)
326  {
327  return Send(Data, DataSize);
328  }
329  else
330  {
331  return Socket::Done;
332  }
333 }
334 
335 
340 Socket::Status SocketTCP::Receive(Packet& PacketToReceive)
341 {
342  // We start by getting the size of the incoming packet
343  Uint32 PacketSize = 0;
344  std::size_t Received = 0;
345  if (myPendingPacketSize < 0)
346  {
347  // Loop until we've received the entire size of the packet
348  // (even a 4 bytes variable may be received in more than one call)
349  while (myPendingHeaderSize < sizeof(myPendingHeader))
350  {
351  char* Data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
352  Socket::Status Status = Receive(Data, sizeof(myPendingHeader) - myPendingHeaderSize, Received);
353  myPendingHeaderSize += Received;
354 
355  if (Status != Socket::Done)
356  return Status;
357  }
358 
359  PacketSize = ntohl(myPendingHeader);
360  myPendingHeaderSize = 0;
361  }
362  else
363  {
364  // There is a pending packet : we already know its size
365  PacketSize = myPendingPacketSize;
366  }
367 
368  // Then loop until we receive all the packet data
369  char Buffer[1024];
370  while (myPendingPacket.size() < PacketSize)
371  {
372  // Receive a chunk of data
373  std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
374  Socket::Status Status = Receive(Buffer, SizeToGet, Received);
375  if (Status != Socket::Done)
376  {
377  // We must save the size of the pending packet until we can receive its content
378  if (Status == Socket::NotReady)
379  myPendingPacketSize = PacketSize;
380  return Status;
381  }
382 
383  // Append it into the packet
384  if (Received > 0)
385  {
386  myPendingPacket.resize(myPendingPacket.size() + Received);
387  char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
388  memcpy(Begin, Buffer, Received);
389  }
390  }
391 
392  // We have received all the datas : we can copy it to the user packet, and clear our internal packet
393  PacketToReceive.Clear();
394  if (!myPendingPacket.empty())
395  PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
396  myPendingPacket.clear();
397  myPendingPacketSize = -1;
398 
399  return Socket::Done;
400 }
401 
402 
407 {
408  if (IsValid())
409  {
410  if (!SocketHelper::Close(mySocket))
411  {
412  std::cerr << "Failed to close socket" << std::endl;
413  return false;
414  }
415 
416  mySocket = SocketHelper::InvalidSocket();
417  }
418 
419  myIsBlocking = true;
420 
421  return true;
422 }
423 
424 
429 bool SocketTCP::IsValid() const
430 {
431  return mySocket != SocketHelper::InvalidSocket();
432 }
433 
434 
438 bool SocketTCP::operator ==(const SocketTCP& Other) const
439 {
440  return mySocket == Other.mySocket;
441 }
442 
443 
447 bool SocketTCP::operator !=(const SocketTCP& Other) const
448 {
449  return mySocket != Other.mySocket;
450 }
451 
452 
458 bool SocketTCP::operator <(const SocketTCP& Other) const
459 {
460  return mySocket < Other.mySocket;
461 }
462 
463 
468 SocketTCP::SocketTCP(SocketHelper::SocketType Descriptor)
469 {
470  Create(Descriptor);
471 }
472 
473 
477 void SocketTCP::Create(SocketHelper::SocketType Descriptor)
478 {
479  // Use the given socket descriptor, or get a new one
480  mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_STREAM, 0);
481  myIsBlocking = true;
482 
483  // Reset the pending packet
484  myPendingHeaderSize = 0;
485  myPendingPacket.clear();
486  myPendingPacketSize = -1;
487 
488  // Setup default options
489  if (IsValid())
490  {
491  // To avoid the "Address already in use" error message when trying to bind to the same port
492  int Yes = 1;
493  if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
494  {
495  std::cerr << "Failed to set socket option \"SO_REUSEADDR\" ; "
496  << "binding to a same port may fail if too fast" << std::endl;
497  }
498 
499  // Disable the Nagle algorithm (ie. removes buffering of TCP packets)
500  if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
501  {
502  std::cerr << "Failed to set socket option \"TCP_NODELAY\" ; "
503  << "all your TCP packets will be buffered" << std::endl;
504  }
505 
506  // Set blocking by default (should always be the case anyway)
507  SetBlocking(true);
508  }
509 }
510 
511 } // namespace sf
void SetBlocking(bool Blocking)
Change the blocking state of the socket.
Definition: SocketTCP.cpp:56
static Socket::Status GetErrorStatus()
Get the last socket error status.
bool operator!=(const SocketTCP &Other) const
Comparison operator !=.
Definition: SocketTCP.cpp:447
Packet wraps data to send / to receive through the network.
Definition: Packet.hpp:41
bool operator==(const SocketTCP &Other) const
Comparison operator ==.
Definition: SocketTCP.cpp:438
std::string ToString() const
Get a string representation of the address.
Definition: IPAddress.cpp:135
Socket::Status Receive(char *Data, std::size_t MaxSize, std::size_t &SizeReceived)
Receive an array of bytes from the host (must be connected first).
Definition: SocketTCP.cpp:272
Socket::Status Send(const char *Data, std::size_t Size)
Send an array of bytes to the host (must be connected first)
Definition: SocketTCP.cpp:235
SocketTCP()
Default constructor.
Definition: SocketTCP.cpp:47
Socket::Status Accept(SocketTCP &Connected, IPAddress *Address=NULL)
Wait for a connection (must be listening to a port).
Definition: SocketTCP.cpp:206
bool Listen(unsigned short Port)
Listen to a specified port for incoming data or connections.
Definition: SocketTCP.cpp:169
Socket::Status Connect(unsigned short Port, const IPAddress &HostAddress, float Timeout=0.f)
Connect to another computer on a specified port.
Definition: SocketTCP.cpp:70
static SocketType InvalidSocket()
Return the value of the invalid socket.
bool operator<(const SocketTCP &Other) const
Comparison operator &lt;.
Definition: SocketTCP.cpp:458
void Clear()
Clear the packet data.
Definition: Packet.cpp:72
IPAddress provides easy manipulation of IP v4 addresses.
Definition: IPAddress.hpp:42
Selector allow reading from multiple sockets without blocking.
Definition: Selector.hpp:44
SocketTCP wraps a socket using TCP protocol to send data safely (but a bit slower) ...
Definition: SocketTCP.hpp:45
static void SetBlocking(SocketType Socket, bool Block)
Set a socket as blocking or non-blocking.
bool Close()
Close the socket.
Definition: SocketTCP.cpp:406
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: SocketTCP.cpp:429
static bool Close(SocketType Socket)
Close / destroy a socket.