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 <SFML/System/Err.hpp>
00032 #include <algorithm>
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 sockAddr;
00076             sockAddr.sin_family      = AF_INET;
00077             sockAddr.sin_port        = htons(port);
00078             sockAddr.sin_addr.s_addr = INADDR_ANY;
00079             memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
00080 
00081             // Bind the socket to the port
00082             if (bind(mySocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) == -1)
00083             {
00084                 Err() << "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 sizeInBytes, 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 && sizeInBytes)
00126     {
00127         // Build the target address
00128         sockaddr_in sockAddr;
00129         sockAddr.sin_family      = AF_INET;
00130         sockAddr.sin_port        = htons(port);
00131         sockAddr.sin_addr.s_addr = inet_addr(address.ToString().c_str());
00132         memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
00133 
00134         // Loop until every byte has been sent
00135         int sent = 0;
00136         int sizeToSend = static_cast<int>(sizeInBytes);
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*>(&sockAddr), sizeof(sockAddr));
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         Err() << "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         Err() << "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 sockAddr;
00183         sockAddr.sin_family      = AF_INET;
00184         sockAddr.sin_port        = 0;
00185         sockAddr.sin_addr.s_addr = INADDR_ANY;
00186         memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
00187         SocketHelper::LengthType sockAddrSize = sizeof(sockAddr);
00188 
00189         // Receive a chunk of bytes
00190         int received = recvfrom(mySocket, data, static_cast<int>(maxSize), 0, reinterpret_cast<sockaddr*>(&sockAddr), &sockAddrSize);
00191 
00192         // Check the number of bytes received
00193         if (received > 0)
00194         {
00195             address = IPAddress(inet_ntoa(sockAddr.sin_addr));
00196             port = ntohs(sockAddr.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         Err() << "Cannot receive data from the network (invalid parameters)" << std::endl;
00211         return Socket::Error;
00212     }
00213 }
00214 
00215 
00219 Socket::Status SocketUDP::Send(Packet& packet, 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 = packet.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& packet, 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     packet.Clear();
00304     if (!myPendingPacket.empty())
00305         packet.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             Err() << "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             Err() << "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             Err() << "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

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