SFML logo
  • Main Page
  • Namespaces
  • Classes
  • Files
  • File List

SocketTCP.cpp

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 (would mean the connection has been accepted)
00135             if (select(static_cast<int>(mySocket + 1), NULL, &Selector, NULL, &Time) > 0)
00136             {
00137                 // Connection succeeded
00138                 Status = Socket::Done;
00139             }
00140             else
00141             {
00142                 // Failed to connect before timeout is over
00143                 Status = SocketHelper::GetErrorStatus();
00144             }
00145         }
00146 
00147         // Switch back to blocking mode
00148         SetBlocking(true);
00149 
00150         return Status;
00151     }
00152 }
00153 
00154 
00158 bool SocketTCP::Listen(unsigned short Port)
00159 {
00160     // Make sure our socket is valid
00161     if (!IsValid())
00162         Create();
00163 
00164     // Build the address
00165     sockaddr_in SockAddr;
00166     memset(SockAddr.sin_zero, 0, sizeof(SockAddr.sin_zero));
00167     SockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
00168     SockAddr.sin_family      = AF_INET;
00169     SockAddr.sin_port        = htons(Port);
00170 
00171     // Bind the socket to the specified port
00172     if (bind(mySocket, reinterpret_cast<sockaddr*>(&SockAddr), sizeof(SockAddr)) == -1)
00173     {
00174         // Not likely to happen, but...
00175         std::cerr << "Failed to bind socket to port " << Port << std::endl;
00176         return false;
00177     }
00178 
00179     // Listen to the bound port
00180     if (listen(mySocket, 0) == -1)
00181     {
00182         // Oops, socket is deaf
00183         std::cerr << "Failed to listen to port " << Port << std::endl;
00184         return false;
00185     }
00186 
00187     return true;
00188 }
00189 
00190 
00195 Socket::Status SocketTCP::Accept(SocketTCP& Connected, IPAddress* Address)
00196 {
00197     // Address that will be filled with client informations
00198     sockaddr_in ClientAddress;
00199     SocketHelper::LengthType Length = sizeof(ClientAddress);
00200 
00201     // Accept a new connection
00202     Connected = accept(mySocket, reinterpret_cast<sockaddr*>(&ClientAddress), &Length);
00203 
00204     // Check errors
00205     if (!Connected.IsValid())
00206     {
00207         if (Address)
00208             *Address = IPAddress();
00209 
00210         return SocketHelper::GetErrorStatus();
00211     }
00212 
00213     // Fill address if requested
00214     if (Address)
00215         *Address = IPAddress(inet_ntoa(ClientAddress.sin_addr));
00216 
00217     return Socket::Done;
00218 }
00219 
00220 
00224 Socket::Status SocketTCP::Send(const char* Data, std::size_t Size)
00225 {
00226     // First check that socket is valid
00227     if (!IsValid())
00228         return Socket::Error;
00229 
00230     // Check parameters
00231     if (Data && Size)
00232     {
00233         // Loop until every byte has been sent
00234         int Sent = 0;
00235         int SizeToSend = static_cast<int>(Size);
00236         for (int Length = 0; Length < SizeToSend; Length += Sent)
00237         {
00238             // Send a chunk of data
00239             Sent = send(mySocket, Data + Length, SizeToSend - Length, 0);
00240 
00241             // Check if an error occured
00242             if (Sent <= 0)
00243                 return SocketHelper::GetErrorStatus();
00244         }
00245 
00246         return Socket::Done;
00247     }
00248     else
00249     {
00250         // Error...
00251         std::cerr << "Cannot send data over the network (invalid parameters)" << std::endl;
00252         return Socket::Error;
00253     }
00254 }
00255 
00256 
00261 Socket::Status SocketTCP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived)
00262 {
00263     // First clear the size received
00264     SizeReceived = 0;
00265 
00266     // Check that socket is valid
00267     if (!IsValid())
00268         return Socket::Error;
00269 
00270     // Check parameters
00271     if (Data && MaxSize)
00272     {
00273         // Receive a chunk of bytes
00274         int Received = recv(mySocket, Data, static_cast<int>(MaxSize), 0);
00275 
00276         // Check the number of bytes received
00277         if (Received > 0)
00278         {
00279             SizeReceived = static_cast<std::size_t>(Received);
00280             return Socket::Done;
00281         }
00282         else if (Received == 0)
00283         {
00284             return Socket::Disconnected;
00285         }
00286         else
00287         {
00288             return SocketHelper::GetErrorStatus();
00289         }
00290     }
00291     else
00292     {
00293         // Error...
00294         std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
00295         return Socket::Error;
00296     }
00297 }
00298 
00299 
00303 Socket::Status SocketTCP::Send(Packet& PacketToSend)
00304 {
00305     // Get the data to send from the packet
00306     std::size_t DataSize = 0;
00307     const char* Data = PacketToSend.OnSend(DataSize);
00308 
00309     // Send the packet size
00310     Uint32 PacketSize = htonl(static_cast<unsigned long>(DataSize));
00311     Send(reinterpret_cast<const char*>(&PacketSize), sizeof(PacketSize));
00312 
00313     // Send the packet data
00314     if (PacketSize > 0)
00315     {
00316         return Send(Data, DataSize);
00317     }
00318     else
00319     {
00320         return Socket::Done;
00321     }
00322 }
00323 
00324 
00329 Socket::Status SocketTCP::Receive(Packet& PacketToReceive)
00330 {
00331     // We start by getting the size of the incoming packet
00332     Uint32      PacketSize = 0;
00333     std::size_t Received   = 0;
00334     if (myPendingPacketSize < 0)
00335     {
00336         Socket::Status Status = Receive(reinterpret_cast<char*>(&PacketSize), sizeof(PacketSize), Received);
00337         if (Status != Socket::Done)
00338             return Status;
00339 
00340         PacketSize = ntohl(PacketSize);
00341     }
00342     else
00343     {
00344         // There is a pending packet : we already know its size
00345         PacketSize = myPendingPacketSize;
00346     }
00347 
00348     // Then loop until we receive all the packet data
00349     char Buffer[1024];
00350     while (myPendingPacket.size() < PacketSize)
00351     {
00352         // Receive a chunk of data
00353         std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
00354         Socket::Status Status = Receive(Buffer, SizeToGet, Received);
00355         if (Status != Socket::Done)
00356         {
00357             // We must save the size of the pending packet until we can receive its content
00358             if (Status == Socket::NotReady)
00359                 myPendingPacketSize = PacketSize;
00360             return Status;
00361         }
00362 
00363         // Append it into the packet
00364         if (Received > 0)
00365         {
00366             myPendingPacket.resize(myPendingPacket.size() + Received);
00367             char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
00368             memcpy(Begin, Buffer, Received);
00369         }
00370     }
00371 
00372     // We have received all the datas : we can copy it to the user packet, and clear our internal packet
00373     PacketToReceive.Clear();
00374     if (!myPendingPacket.empty())
00375         PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
00376     myPendingPacket.clear();
00377     myPendingPacketSize = -1;
00378 
00379     return Socket::Done;
00380 }
00381 
00382 
00386 bool SocketTCP::Close()
00387 {
00388     if (IsValid())
00389     {
00390         if (!SocketHelper::Close(mySocket))
00391         {
00392             std::cerr << "Failed to close socket" << std::endl;
00393             return false;
00394         }
00395 
00396         mySocket = SocketHelper::InvalidSocket();
00397     }
00398 
00399     myIsBlocking = true;
00400 
00401     return true;
00402 }
00403 
00404 
00409 bool SocketTCP::IsValid() const
00410 {
00411     return mySocket != SocketHelper::InvalidSocket();
00412 }
00413 
00414 
00418 bool SocketTCP::operator ==(const SocketTCP& Other) const
00419 {
00420     return mySocket == Other.mySocket;
00421 }
00422 
00423 
00427 bool SocketTCP::operator !=(const SocketTCP& Other) const
00428 {
00429     return mySocket != Other.mySocket;
00430 }
00431 
00432 
00438 bool SocketTCP::operator <(const SocketTCP& Other) const
00439 {
00440     return mySocket < Other.mySocket;
00441 }
00442 
00443 
00448 SocketTCP::SocketTCP(SocketHelper::SocketType Descriptor)
00449 {
00450     Create(Descriptor);
00451 }
00452 
00453 
00457 void SocketTCP::Create(SocketHelper::SocketType Descriptor)
00458 {
00459     // Use the given socket descriptor, or get a new one
00460     mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_STREAM, 0);
00461     myIsBlocking = true;
00462 
00463     // Reset the pending packet
00464     myPendingPacket.clear();
00465     myPendingPacketSize = -1;
00466 
00467     // Setup default options
00468     if (IsValid())
00469     {
00470         // To avoid the "Address already in use" error message when trying to bind to the same port
00471         int Yes = 1;
00472         if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00473         {
00474             std::cerr << "Failed to set socket option \"SO_REUSEADDR\" ; "
00475                       << "binding to a same port may fail if too fast" << std::endl;
00476         }
00477 
00478         // Disable the Nagle algorithm (ie. removes buffering of TCP packets)
00479         if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00480         {
00481             std::cerr << "Failed to set socket option \"TCP_NODELAY\" ; "
00482                       << "all your TCP packets will be buffered" << std::endl;
00483         }
00484 
00485         // Set blocking by default (should always be the case anyway)
00486         SetBlocking(true);
00487     }
00488 }
00489 
00490 } // namespace sf

 ::  Copyright © 2007-2008 Laurent Gomila, all rights reserved  ::  Documentation generated by doxygen 1.5.2  ::