SocketTCP.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/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
42namespace sf
43{
51
52
56void 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
70Socket::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
149 }
150 }
151 else
152 {
153 // Failed to connect before timeout is over
155 }
156 }
157
158 // Switch back to blocking mode
159 SetBlocking(true);
160
161 return Status;
162 }
163}
164
165
169bool 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
206Socket::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
235Socket::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
272Socket::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
314Socket::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
340Socket::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
430{
431 return mySocket != SocketHelper::InvalidSocket();
432}
433
434
438bool SocketTCP::operator ==(const SocketTCP& Other) const
439{
440 return mySocket == Other.mySocket;
441}
442
443
447bool SocketTCP::operator !=(const SocketTCP& Other) const
448{
449 return mySocket != Other.mySocket;
450}
451
452
458bool SocketTCP::operator <(const SocketTCP& Other) const
459{
460 return mySocket < Other.mySocket;
461}
462
463
468SocketTCP::SocketTCP(SocketHelper::SocketType Descriptor)
469{
470 Create(Descriptor);
471}
472
473
477void 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
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
Selector allow reading from multiple sockets without blocking.
Definition Selector.hpp:45
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.
SocketTCP()
Default constructor.
Definition SocketTCP.cpp:47
bool IsValid() const
Check if the socket is in a valid state ; this function can be called any time to check if the socket...
bool Listen(unsigned short Port)
Listen to a specified port for incoming data or connections.
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).
Socket::Status Send(const char *Data, std::size_t Size)
Send an array of bytes to the host (must be connected first).
bool Close()
Close the socket.
Socket::Status Accept(SocketTCP &Connected, IPAddress *Address=NULL)
Wait for a connection (must be listening to a port).
void SetBlocking(bool Blocking)
Change the blocking state of the socket.
Definition SocketTCP.cpp:56
bool operator!=(const SocketTCP &Other) const
Comparison operator !=.
bool operator==(const SocketTCP &Other) const
Comparison operator ==.
bool operator<(const SocketTCP &Other) const
Comparison operator <.
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