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

SocketUDP.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/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     // This is not safe at all, as data can be lost, duplicated, or arrive in a different order.
00248     // So if a packet is split into more than one chunk, nobody knows what could happen...
00249     // Conclusion : we shouldn't use packets with UDP, unless we build a more complex protocol on top of it.
00250 
00251     // We start by getting the size of the incoming packet
00252     Uint32      PacketSize = 0;
00253     std::size_t Received   = 0;
00254     if (myPendingPacketSize < 0)
00255     {
00256         Socket::Status Status = Receive(reinterpret_cast<char*>(&PacketSize), sizeof(PacketSize), Received, Address, Port);
00257         if (Status != Socket::Done)
00258             return Status;
00259 
00260         PacketSize = ntohl(PacketSize);
00261     }
00262     else
00263     {
00264         // There is a pending packet : we already know its size
00265         PacketSize = myPendingPacketSize;
00266     }
00267 
00268     // Clear the user packet
00269     PacketToReceive.Clear();
00270 
00271     // Use another address instance for receiving the packet data ;
00272     // chunks of data coming from a different sender will be discarded (and lost...)
00273     IPAddress Sender;
00274     unsigned short SenderPort;
00275 
00276     // Then loop until we receive all the packet data
00277     char Buffer[1024];
00278     while (myPendingPacket.size() < PacketSize)
00279     {
00280         // Receive a chunk of data
00281         std::size_t SizeToGet = std::min(static_cast<std::size_t>(PacketSize - myPendingPacket.size()), sizeof(Buffer));
00282         Socket::Status Status = Receive(Buffer, SizeToGet, Received, Sender, SenderPort);
00283         if (Status != Socket::Done)
00284         {
00285             // We must save the size of the pending packet until we can receive its content
00286             if (Status == Socket::NotReady)
00287                 myPendingPacketSize = PacketSize;
00288             return Status;
00289         }
00290 
00291         // Append it into the packet
00292         if ((Sender == Address) && (SenderPort == Port) && (Received > 0))
00293         {
00294             myPendingPacket.resize(myPendingPacket.size() + Received);
00295             char* Begin = &myPendingPacket[0] + myPendingPacket.size() - Received;
00296             memcpy(Begin, Buffer, Received);
00297         }
00298     }
00299 
00300     // We have received all the datas : we can copy it to the user packet, and clear our internal packet
00301     PacketToReceive.Clear();
00302     if (!myPendingPacket.empty())
00303         PacketToReceive.OnReceive(&myPendingPacket[0], myPendingPacket.size());
00304     myPendingPacket.clear();
00305     myPendingPacketSize = -1;
00306 
00307     return Socket::Done;
00308 }
00309 
00310 
00314 bool SocketUDP::Close()
00315 {
00316     if (IsValid())
00317     {
00318         if (!SocketHelper::Close(mySocket))
00319         {
00320             std::cerr << "Failed to close socket" << std::endl;
00321             return false;
00322         }
00323 
00324         mySocket = SocketHelper::InvalidSocket();
00325     }
00326 
00327     myPort       = 0;
00328     myIsBlocking = true;
00329 
00330     return true;
00331 }
00332 
00333 
00338 bool SocketUDP::IsValid() const
00339 {
00340     return mySocket != SocketHelper::InvalidSocket();
00341 }
00342 
00343 
00347 unsigned short SocketUDP::GetPort() const
00348 {
00349     return myPort;
00350 }
00351 
00352 
00356 bool SocketUDP::operator ==(const SocketUDP& Other) const
00357 {
00358     return mySocket == Other.mySocket;
00359 }
00360 
00361 
00365 bool SocketUDP::operator !=(const SocketUDP& Other) const
00366 {
00367     return mySocket != Other.mySocket;
00368 }
00369 
00370 
00376 bool SocketUDP::operator <(const SocketUDP& Other) const
00377 {
00378     return mySocket < Other.mySocket;
00379 }
00380 
00381 
00386 SocketUDP::SocketUDP(SocketHelper::SocketType Descriptor)
00387 {
00388     Create(Descriptor);
00389 }
00390 
00391 
00395 void SocketUDP::Create(SocketHelper::SocketType Descriptor)
00396 {
00397     // Use the given socket descriptor, or get a new one
00398     mySocket = Descriptor ? Descriptor : socket(PF_INET, SOCK_DGRAM, 0);
00399     myIsBlocking = true;
00400 
00401     // Clear the last port used
00402     myPort = 0;
00403 
00404     // Reset the pending packet
00405     myPendingPacket.clear();
00406     myPendingPacketSize = -1;
00407 
00408     // Setup default options
00409     if (IsValid())
00410     {
00411         // To avoid the "Address already in use" error message when trying to bind to the same port
00412         int Yes = 1;
00413         if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00414         {
00415             std::cerr << "Failed to set socket option \"reuse address\" ; "
00416                       << "binding to a same port may fail if too fast" << std::endl;
00417         }
00418 
00419         // Enable broadcast by default
00420         if (setsockopt(mySocket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&Yes), sizeof(Yes)) == -1)
00421         {
00422             std::cerr << "Failed to enable broadcast on UDP socket" << std::endl;
00423         }
00424 
00425         // Set blocking by default (should always be the case anyway)
00426         SetBlocking(true);
00427     }
00428 }
00429 
00430 } // namespace sf

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