- 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 ::