Back to index

libsfml  1.6+dfsg2
SocketTCP.cpp
Go to the documentation of this file.
00001 
00002 //
00003 // SFML - Simple and Fast Multimedia Library
00004 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
00005 //
00006 // This software is provided 'as-is', without any express or implied warranty.
00007 // In no event will the authors be held liable for any damages arising from the use of this software.
00008 //
00009 // Permission is granted to anyone to use this software for any purpose,
00010 // including commercial applications, and to alter it and redistribute it freely,
00011 // subject to the following restrictions:
00012 //
00013 // 1. The origin of this software must not be misrepresented;
00014 //    you must not claim that you wrote the original software.
00015 //    If you use this software in a product, an acknowledgment
00016 //    in the product documentation would be appreciated but is not required.
00017 //
00018 // 2. Altered source versions must be plainly marked as such,
00019 //    and must not be misrepresented as being the original software.
00020 //
00021 // 3. This notice may not be removed or altered from any source distribution.
00022 //
00024 
00026 // Headers
00028 #include <SFML/Network/SocketTCP.hpp>
00029 #include <SFML/Network/IPAddress.hpp>
00030 #include <SFML/Network/Packet.hpp>
00031 #include <SFML/Network/SocketHelper.hpp>
00032 #include <algorithm>
00033 #include <iostream>
00034 #include <string.h>
00035 
00036 
00037 #ifdef _MSC_VER
00038     #pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
00039 #endif
00040 
00041 
00042 namespace sf
00043 {
00047 SocketTCP::SocketTCP()
00048 {
00049     Create(SocketHelper::InvalidSocket());
00050 }
00051 
00052 
00056 void SocketTCP::SetBlocking(bool Blocking)
00057 {
00058     // Make sure our socket is valid
00059     if (!IsValid())
00060         Create();
00061 
00062     SocketHelper::SetBlocking(mySocket, Blocking);
00063     myIsBlocking = Blocking;
00064 }
00065 
00066 
00070 Socket::Status SocketTCP::Connect(unsigned short Port, const IPAddress& HostAddress, float Timeout)
00071 {
00072     // Make sure our socket is valid
00073     if (!IsValid())
00074         Create();
00075 
00076     // Build the host address
00077     sockaddr_in SockAddr;
00078     memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
00079     SockAddr.sin_addr.s_addr = inet_addr(HostAddress.ToString().c_str());
00080     SockAddr.sin_family      = AF_INET;
00081     SockAddr.sin_port        = htons(Port);
00082 
00083     if (Timeout <= 0)
00084     {
00085         // ----- We're not using a timeout : just try to connect -----
00086 
00087         if (connect(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
00088         {
00089             // Failed to connect
00090             return SocketHelper::GetErrorStatus();
00091         }
00092 
00093         // Connection succeeded
00094         return Socket::Done;
00095     }
00096     else
00097     {
00098         // ----- We're using a timeout : we'll need a few tricks to make it work -----
00099 
00100         // Save the previous blocking state
00101         bool IsBlocking = myIsBlocking;
00102 
00103         // Switch to non-blocking to enable our connection timeout
00104         if (IsBlocking)
00105             SetBlocking(false);
00106 
00107         // Try to connect to host
00108         if (connect(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) >= 0)
00109         {
00110             // We got instantly connected! (it may no happen a lot...)
00111             return Socket::Done;
00112         }
00113 
00114         // Get the error status
00115         Socket::Status Status = SocketHelper::GetErrorStatus();
00116 
00117         // If we were in non-blocking mode, return immediatly
00118         if (!IsBlocking)
00119             return Status;
00120 
00121         // Otherwise, wait until something happens to our socket (success, timeout or error)
00122         if (Status == Socket::NotReady)
00123         {
00124             // Setup the selector
00125             fd_set Selector;
00126             FD_ZERO(&Selector);
00127             FD_SET(mySocket, &Selector);
00128 
00129             // Setup the timeout
00130             timeval Time;
00131             Time.tv_sec  = static_cast<long>(Timeout);
00132             Time.tv_usec = (static_cast<long>(Timeout * 1000) % 1000) * 1000;
00133 
00134             // Wait for something to write on our socket (which means that the connection request has returned)
00135             if (select(static_cast<int>(mySocket + 1), NULL, &Selector, NULL, &Time) > 0)
00136             {
00137                 // At this point the connection may have been either accepted or refused.
00138                 // To know whether it's a success or a failure, we try to retrieve the name of the connected peer
00139                 SocketHelper::LengthType Size = sizeof(SockAddr);
00140                 if (getpeername(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), &Size) != -1)
00141                 {
00142                     // Connection accepted
00143                     Status = Socket::Done;
00144                 }
00145                 else
00146                 {
00147                     // Connection failed
00148                     Status = SocketHelper::GetErrorStatus();
00149                 }
00150             }
00151             else
00152             {
00153                 // Failed to connect before timeout is over
00154                 Status = SocketHelper::GetErrorStatus();
00155             }
00156         }
00157 
00158         // Switch back to blocking mode
00159         SetBlocking(true);
00160 
00161         return Status;
00162     }
00163 }
00164 
00165 
00169 bool SocketTCP::Listen(unsigned short Port)
00170 {
00171     // Make sure our socket is valid
00172     if (!IsValid())
00173         Create();
00174 
00175     // Build the address
00176     sockaddr_in SockAddr;
00177     memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
00178     SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
00179     SockAddr.sin_family      = AF_INET;
00180     SockAddr.sin_port        = htons(Port);
00181 
00182     // Bind the socket to the specified port
00183     if (bind(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
00184     {
00185         // Not likely to happen, but...
00186         std::cerr << "Failed to bind socket to port " << Port << std::endl;
00187         return false;
00188     }
00189 
00190     // Listen to the bound port
00191     if (listen(mySocket, 0) == -1)
00192     {
00193         // Oops, socket is deaf
00194         std::cerr << "Failed to listen to port " << Port << std::endl;
00195         return false;
00196     }
00197 
00198     return true;
00199 }
00200 
00201 
00206 Socket::Status SocketTCP::Accept(SocketTCP& Connected, IPAddress* Address)
00207 {
00208     // Address that will be filled with client informations
00209     sockaddr_in ClientAddress;
00210     SocketHelper::LengthType Length = sizeof(ClientAddress);
00211 
00212     // Accept a new connection
00213     Connected = accept(mySocket, reinterpret_cast<sockaddr*>(&ClientAddress), &Length);
00214 
00215     // Check errors
00216     if (!Connected.IsValid())
00217     {
00218         if (Address)
00219             *Address = IPAddress();
00220 
00221         return SocketHelper::GetErrorStatus();
00222     }
00223 
00224     // Fill address if requested
00225     if (Address)
00226         *Address = IPAddress(inet_ntoa(ClientAddress.sin_addr));
00227 
00228     return Socket::Done;
00229 }
00230 
00231 
00235 Socket::Status SocketTCP::Send(const char* Data, std::size_t Size)
00236 {
00237     // First check that socket is valid
00238     if (!IsValid())
00239         return Socket::Error;
00240 
00241     // Check parameters
00242     if (Data && Size)
00243     {
00244         // Loop until every byte has been sent
00245         int Sent = 0;
00246         int SizeToSend = static_cast<int>(Size);
00247         for (int Length = 0; Length < SizeToSend; Length += Sent)
00248         {
00249             // Send a chunk of data
00250             Sent = send(mySocket, Data + Length, SizeToSend - Length, 0);
00251 
00252             // Check if an error occured
00253             if (Sent <= 0)
00254                 return SocketHelper::GetErrorStatus();
00255         }
00256 
00257         return Socket::Done;
00258     }
00259     else
00260     {
00261         // Error...
00262         std::cerr << "Cannot send data over the network (invalid parameters)" << std::endl;
00263         return Socket::Error;
00264     }
00265 }
00266 
00267 
00272 Socket::Status SocketTCP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived)
00273 {
00274     // First clear the size received
00275     SizeReceived = 0;
00276 
00277     // Check that socket is valid
00278     if (!IsValid())
00279         return Socket::Error;
00280 
00281     // Check parameters
00282     if (Data && MaxSize)
00283     {
00284         // Receive a chunk of bytes
00285         int Received = recv(mySocket, Data, static_cast<int>(MaxSize), 0);
00286 
00287         // Check the number of bytes received
00288         if (Received > 0)
00289         {
00290             SizeReceived = static_cast<std::size_t>(Received);
00291             return Socket::Done;
00292         }
00293         else if (Received == 0)
00294         {
00295             return Socket::Disconnected;
00296         }
00297         else
00298         {
00299             return SocketHelper::GetErrorStatus();
00300         }
00301     }
00302     else
00303     {
00304         // Error...
00305         std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
00306         return Socket::Error;
00307     }
00308 }
00309 
00310 
00314 Socket::Status SocketTCP::Send(Packet& PacketToSend)
00315 {
00316     // Get the data to send from the packet
00317     std::size_t DataSize = 0;
00318     const char* Data = PacketToSend.OnSend(DataSize);
00319 
00320     // Send the packet size
00321     Uint32 PacketSize = htonl(static_cast<unsigned long>(DataSize));
00322     Send(reinterpret_cast<const char*>(&PacketSize), sizeof(PacketSize));
00323 
00324     // Send the packet data
00325     if (PacketSize > 0)
00326     {
00327         return Send(Data, DataSize);
00328     }
00329     else
00330     {
00331         return Socket::Done;
00332     }
00333 }
00334 
00335 
00340 Socket::Status SocketTCP::Receive(Packet& PacketToReceive)
00341 {
00342     // We start by getting the size of the incoming packet
00343     Uint32      PacketSize = 0;
00344     std::size_t Received   = 0;
00345     if (myPendingPacketSize < 0)
00346     {
00347         // Loop until we've received the entire size of the packet
00348         // (even a 4 bytes variable may be received in more than one call)
00349         while (myPendingHeaderSize < sizeof(myPendingHeader))
00350         {
00351             char* Data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
00352             Socket::Status Status = Receive(Data, sizeof(myPendingHeader) - myPendingHeaderSize, Received);
00353             myPendingHeaderSize += Received;
00354 
00355             if (Status != Socket::Done)
00356                 return Status;
00357         }
00358 
00359         PacketSize = ntohl(myPendingHeader);
00360         myPendingHeaderSize = 0;
00361     }
00362     else
00363     {
00364         // There is a pending packet : we already know its size
00365         PacketSize = myPendingPacketSize;
00366     }
00367 
00368     // Then loop until we receive all the packet data
00369     char Buffer[1024];
00370     while (myPendingPacket.size() < PacketSize)
00371     {
00372         // Receive a chunk of data
00373         std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
00374         Socket::Status Status = Receive(Buffer, SizeToGet, Received);
00375         if (Status != Socket::Done)
00376         {
00377             // We must save the size of the pending packet until we can receive its content
00378             if (Status == Socket::NotReady)
00379                 myPendingPacketSize = PacketSize;
00380             return Status;
00381         }
00382 
00383         // Append it into the packet
00384         if (Received > 0)
00385         {
00386             myPendingPacket.resize(myPendingPacket.size() + Received);
00387             char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
00388             memcpy(Begin, Buffer, Received);
00389         }
00390     }
00391 
00392     // We have received all the datas : we can copy it to the user packet, and clear our internal packet
00393     PacketToReceive.Clear();
00394     if (!myPendingPacket.empty())
00395         PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
00396     myPendingPacket.clear();
00397     myPendingPacketSize = -1;
00398 
00399     return Socket::Done;
00400 }
00401 
00402 
00406 bool SocketTCP::Close()
00407 {
00408     if (IsValid())
00409     {
00410         if (!SocketHelper::Close(mySocket))
00411         {
00412             std::cerr << "Failed to close socket" << std::endl;
00413             return false;
00414         }
00415 
00416         mySocket = SocketHelper::InvalidSocket();
00417     }
00418 
00419     myIsBlocking = true;
00420 
00421     return true;
00422 }
00423 
00424 
00429 bool SocketTCP::IsValid() const
00430 {
00431     return mySocket != SocketHelper::InvalidSocket();
00432 }
00433 
00434 
00438 bool SocketTCP::operator ==(const SocketTCP& Other) const
00439 {
00440     return mySocket == Other.mySocket;
00441 }
00442 
00443 
00447 bool SocketTCP::operator !=(const SocketTCP& Other) const
00448 {
00449     return mySocket != Other.mySocket;
00450 }
00451 
00452 
00458 bool SocketTCP::operator <(const SocketTCP& Other) const
00459 {
00460     return mySocket < Other.mySocket;
00461 }
00462 
00463 
00468 SocketTCP::SocketTCP(SocketHelper::SocketType Descriptor)
00469 {
00470     Create(Descriptor);
00471 }
00472 
00473 
00477 void SocketTCP::Create(SocketHelper::SocketType Descriptor)
00478 {
00479     // Use the given socket descriptor, or get a new one
00480     mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_STREAM, 0);
00481     myIsBlocking = true;
00482 
00483     // Reset the pending packet
00484     myPendingHeaderSize = 0;
00485     myPendingPacket.clear();
00486     myPendingPacketSize = -1;
00487 
00488     // Setup default options
00489     if (IsValid())
00490     {
00491         // To avoid the "Address already in use" error message when trying to bind to the same port
00492         int Yes = 1;
00493         if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00494         {
00495             std::cerr << "Failed to set socket option \"SO_REUSEADDR\" ; "
00496                       << "binding to a same port may fail if too fast" << std::endl;
00497         }
00498 
00499         // Disable the Nagle algorithm (ie. removes buffering of TCP packets)
00500         if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00501         {
00502             std::cerr << "Failed to set socket option \"TCP_NODELAY\" ; "
00503                       << "all your TCP packets will be buffered" << std::endl;
00504         }
00505 
00506         // Set blocking by default (should always be the case anyway)
00507         SetBlocking(true);
00508     }
00509 }
00510 
00511 } // namespace sf