Back to index

libsfml  1.6+dfsg2
SocketUDP.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/SocketUDP.hpp>
00029 #include <SFML/Network/IPAddress.hpp>
00030 #include <SFML/Network/Packet.hpp>
00031 #include <algorithm>
00032 #include <iostream>
00033 #include <string.h>
00034 
00035 
00036 namespace sf
00037 {
00041 SocketUDP::SocketUDP()
00042 {
00043     Create();
00044 }
00045 
00046 
00050 void SocketUDP::SetBlocking(bool Blocking)
00051 {
00052     // Make sure our socket is valid
00053     if (!IsValid())
00054         Create();
00055 
00056     SocketHelper::SetBlocking(mySocket, Blocking);
00057     myIsBlocking = Blocking;
00058 }
00059 
00060 
00064 bool SocketUDP::Bind(unsigned short Port)
00065 {
00066     // Check if the socket is already bound to the specified port
00067     if (myPort != Port)
00068     {
00069         // If the socket was previously bound to another port, we need to unbind it first
00070         Unbind();
00071 
00072         if (Port != 0)
00073         {
00074             // Build an address with the specified port
00075             sockaddr_in Addr;
00076             Addr.sin_family      = AF_INET;
00077             Addr.sin_port        = htons(Port);
00078             Addr.sin_addr.s_addr = INADDR_ANY;
00079             memset(Addr.sin_zero, 0, sizeof(Addr.sin_zero));
00080 
00081             // Bind the socket to the port
00082             if (bind(mySocket, reinterpret_cast<sockaddr*>(&Addr), sizeof(Addr)) == -1)
00083             {
00084                 std::cerr << "Failed to bind the socket to port " << Port << std::endl;
00085                 myPort = 0;
00086                 return false;
00087             }
00088         }
00089 
00090         // Save the new port
00091         myPort = Port;
00092     }
00093 
00094     return true;
00095 }
00096 
00097 
00101 bool SocketUDP::Unbind()
00102 {
00103     // To unbind the socket, we just recreate it
00104     if (myPort != 0)
00105     {
00106         Close();
00107         Create();
00108         myPort = 0;
00109     }
00110 
00111     return true;
00112 }
00113 
00114 
00118 Socket::Status SocketUDP::Send(const char* Data, std::size_t Size, const IPAddress& Address, unsigned short Port)
00119 {
00120     // Make sure the socket is valid
00121     if (!IsValid())
00122         Create();
00123 
00124     // Check parameters
00125     if (Data && Size)
00126     {
00127         // Build the target address
00128         sockaddr_in Target;
00129         Target.sin_family      = AF_INET;
00130         Target.sin_port        = htons(Port);
00131         Target.sin_addr.s_addr = inet_addr(Address.ToString().c_str());
00132         memset(Target.sin_zero, 0, sizeof(Target.sin_zero));
00133 
00134         // Loop until every byte has been sent
00135         int Sent = 0;
00136         int SizeToSend = static_cast<int>(Size);
00137         for (int Length = 0; Length < SizeToSend; Length += Sent)
00138         {
00139             // Send a chunk of data
00140             Sent = sendto(mySocket, Data + Length, SizeToSend - Length, 0, reinterpret_cast<sockaddr*>(&Target), sizeof(Target));
00141 
00142             // Check errors
00143             if (Sent <= 0)
00144                 return SocketHelper::GetErrorStatus();
00145         }
00146 
00147         return Socket::Done;
00148     }
00149     else
00150     {
00151         // Error...
00152         std::cerr << "Cannot send data over the network (invalid parameters)" << std::endl;
00153         return Socket::Error;
00154     }
00155 }
00156 
00157 
00162 Socket::Status SocketUDP::Receive(char* Data, std::size_t MaxSize, std::size_t& SizeReceived, IPAddress& Address, unsigned short& Port)
00163 {
00164     // First clear the size received
00165     SizeReceived = 0;
00166 
00167     // Make sure the socket is bound to a port
00168     if (myPort == 0)
00169     {
00170         std::cerr << "Failed to receive data ; the UDP socket first needs to be bound to a port" << std::endl;
00171         return Socket::Error;
00172     }
00173 
00174     // Make sure the socket is valid
00175     if (!IsValid())
00176         Create();
00177 
00178     // Check parameters
00179     if (Data && MaxSize)
00180     {
00181         // Data that will be filled with the other computer's address
00182         sockaddr_in Sender;
00183         Sender.sin_family      = AF_INET;
00184         Sender.sin_port        = 0;
00185         Sender.sin_addr.s_addr = INADDR_ANY;
00186         memset(Sender.sin_zero, 0, sizeof(Sender.sin_zero));
00187         SocketHelper::LengthType SenderSize = sizeof(Sender);
00188 
00189         // Receive a chunk of bytes
00190         int Received = recvfrom(mySocket, Data, static_cast<int>(MaxSize), 0, reinterpret_cast<sockaddr*>(&Sender), &SenderSize);
00191 
00192         // Check the number of bytes received
00193         if (Received > 0)
00194         {
00195             Address = IPAddress(inet_ntoa(Sender.sin_addr));
00196             Port = ntohs(Sender.sin_port);
00197             SizeReceived = static_cast<std::size_t>(Received);
00198             return Socket::Done;
00199         }
00200         else
00201         {
00202             Address = IPAddress();
00203             Port = 0;
00204             return Received == 0 ? Socket::Disconnected : SocketHelper::GetErrorStatus();
00205         }
00206     }
00207     else
00208     {
00209         // Error...
00210         std::cerr << "Cannot receive data from the network (invalid parameters)" << std::endl;
00211         return Socket::Error;
00212     }
00213 }
00214 
00215 
00219 Socket::Status SocketUDP::Send(Packet& PacketToSend, const IPAddress& Address, unsigned short Port)
00220 {
00221     // Get the data to send from the packet
00222     std::size_t DataSize = 0;
00223     const char* Data = PacketToSend.OnSend(DataSize);
00224 
00225     // Send the packet size
00226     Uint32 PacketSize = htonl(static_cast<unsigned long>(DataSize));
00227     Send(reinterpret_cast<const char*>(&PacketSize), sizeof(PacketSize), Address, Port);
00228 
00229     // Send the packet data
00230     if (PacketSize > 0)
00231     {
00232         return Send(Data, DataSize, Address, Port);
00233     }
00234     else
00235     {
00236         return Socket::Done;
00237     }
00238 }
00239 
00240 
00245 Socket::Status SocketUDP::Receive(Packet& PacketToReceive, IPAddress& Address, unsigned short& Port)
00246 {
00247     // We start by getting the size of the incoming packet
00248     Uint32      PacketSize = 0;
00249     std::size_t Received   = 0;
00250     if (myPendingPacketSize < 0)
00251     {
00252         // Loop until we've received the entire size of the packet
00253         // (even a 4 bytes variable may be received in more than one call)
00254         while (myPendingHeaderSize < sizeof(myPendingHeader))
00255         {
00256             char* Data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
00257             Socket::Status Status = Receive(Data, sizeof(myPendingHeader) - myPendingHeaderSize, Received, Address, Port);
00258             myPendingHeaderSize += Received;
00259 
00260             if (Status != Socket::Done)
00261                 return Status;
00262         }
00263 
00264         PacketSize = ntohl(myPendingHeader);
00265         myPendingHeaderSize = 0;
00266     }
00267     else
00268     {
00269         // There is a pending packet : we already know its size
00270         PacketSize = myPendingPacketSize;
00271     }
00272 
00273     // Use another address instance for receiving the packet data ;
00274     // chunks of data coming from a different sender will be discarded (and lost...)
00275     IPAddress Sender;
00276     unsigned short SenderPort;
00277 
00278     // Then loop until we receive all the packet data
00279     char Buffer[1024];
00280     while (myPendingPacket.size() < PacketSize)
00281     {
00282         // Receive a chunk of data
00283         std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
00284         Socket::Status Status = Receive(Buffer, SizeToGet, Received, Sender, SenderPort);
00285         if (Status != Socket::Done)
00286         {
00287             // We must save the size of the pending packet until we can receive its content
00288             if (Status == Socket::NotReady)
00289                 myPendingPacketSize = PacketSize;
00290             return Status;
00291         }
00292 
00293         // Append it into the packet
00294         if ((Sender == Address) && (SenderPort == Port) && (Received > 0))
00295         {
00296             myPendingPacket.resize(myPendingPacket.size() + Received);
00297             char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
00298             memcpy(Begin, Buffer, Received);
00299         }
00300     }
00301 
00302     // We have received all the datas : we can copy it to the user packet, and clear our internal packet
00303     PacketToReceive.Clear();
00304     if (!myPendingPacket.empty())
00305         PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
00306     myPendingPacket.clear();
00307     myPendingPacketSize = -1;
00308 
00309     return Socket::Done;
00310 }
00311 
00312 
00316 bool SocketUDP::Close()
00317 {
00318     if (IsValid())
00319     {
00320         if (!SocketHelper::Close(mySocket))
00321         {
00322             std::cerr << "Failed to close socket" << std::endl;
00323             return false;
00324         }
00325 
00326         mySocket = SocketHelper::InvalidSocket();
00327     }
00328 
00329     myPort       = 0;
00330     myIsBlocking = true;
00331 
00332     return true;
00333 }
00334 
00335 
00340 bool SocketUDP::IsValid() const
00341 {
00342     return mySocket != SocketHelper::InvalidSocket();
00343 }
00344 
00345 
00349 unsigned short SocketUDP::GetPort() const
00350 {
00351     return myPort;
00352 }
00353 
00354 
00358 bool SocketUDP::operator ==(const SocketUDP& Other) const
00359 {
00360     return mySocket == Other.mySocket;
00361 }
00362 
00363 
00367 bool SocketUDP::operator !=(const SocketUDP& Other) const
00368 {
00369     return mySocket != Other.mySocket;
00370 }
00371 
00372 
00378 bool SocketUDP::operator <(const SocketUDP& Other) const
00379 {
00380     return mySocket < Other.mySocket;
00381 }
00382 
00383 
00388 SocketUDP::SocketUDP(SocketHelper::SocketType Descriptor)
00389 {
00390     Create(Descriptor);
00391 }
00392 
00393 
00397 void SocketUDP::Create(SocketHelper::SocketType Descriptor)
00398 {
00399     // Use the given socket descriptor, or get a new one
00400     mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_DGRAM, 0);
00401     myIsBlocking = true;
00402 
00403     // Clear the last port used
00404     myPort = 0;
00405 
00406     // Reset the pending packet
00407     myPendingHeaderSize = 0;
00408     myPendingPacket.clear();
00409     myPendingPacketSize = -1;
00410 
00411     // Setup default options
00412     if (IsValid())
00413     {
00414         // To avoid the "Address already in use" error message when trying to bind to the same port
00415         int Yes = 1;
00416         if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00417         {
00418             std::cerr << "Failed to set socket option \"reuse address\" ; "
00419                       << "binding to a same port may fail if too fast" << std::endl;
00420         }
00421 
00422         // Enable broadcast by default
00423         if (setsockopt(mySocket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00424         {
00425             std::cerr << "Failed to enable broadcast on UDP socket" << std::endl;
00426         }
00427 
00428         // Set blocking by default (should always be the case anyway)
00429         SetBlocking(true);
00430     }
00431 }
00432 
00433 } // namespace sf