FS#86 - Rewrite the sockets API

Updated the API documentation of the whole network module
The system headers are no longer included by the sfml-network public headers

git-svn-id: https://sfml.svn.sourceforge.net/svnroot/sfml/branches/sfml2@1475 4e206d99-4929-0410-ac5d-dfc041789085
This commit is contained in:
LaurentGom 2010-03-23 09:39:43 +00:00
parent a09ee0f9e3
commit 9280771665
66 changed files with 3976 additions and 2992 deletions

View file

@ -36,35 +36,19 @@
namespace sf
{
////////////////////////////////////////////////////////////
// Utility class for exchanging stuff with the server
// on the data channel
////////////////////////////////////////////////////////////
class Ftp::DataChannel : NonCopyable
{
public :
////////////////////////////////////////////////////////////
// Constructor
////////////////////////////////////////////////////////////
DataChannel(Ftp& owner);
////////////////////////////////////////////////////////////
// Destructor
////////////////////////////////////////////////////////////
~DataChannel();
////////////////////////////////////////////////////////////
// Open the data channel using the specified mode and port
////////////////////////////////////////////////////////////
Ftp::Response Open(Ftp::TransferMode mode);
////////////////////////////////////////////////////////////
// Send data on the data channel
////////////////////////////////////////////////////////////
void Send(const std::vector<char>& data);
////////////////////////////////////////////////////////////
// Receive data on the data channel until it is closed
////////////////////////////////////////////////////////////
void Receive(std::vector<char>& data);
@ -74,12 +58,10 @@ private :
// Member data
////////////////////////////////////////////////////////////
Ftp& myFtp; ///< Reference to the owner Ftp instance
SocketTCP myDataSocket; ///< Socket used for data transfers
TcpSocket myDataSocket; ///< Socket used for data transfers
};
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Ftp::Response::Response(Status code, const std::string& message) :
myStatus (code),
@ -89,9 +71,6 @@ myMessage(message)
}
////////////////////////////////////////////////////////////
/// Convenience function to check if the response status code
/// means a success
////////////////////////////////////////////////////////////
bool Ftp::Response::IsOk() const
{
@ -99,8 +78,6 @@ bool Ftp::Response::IsOk() const
}
////////////////////////////////////////////////////////////
/// Get the response status code
////////////////////////////////////////////////////////////
Ftp::Response::Status Ftp::Response::GetStatus() const
{
@ -108,8 +85,6 @@ Ftp::Response::Status Ftp::Response::GetStatus() const
}
////////////////////////////////////////////////////////////
/// Get the full message contained in the response
////////////////////////////////////////////////////////////
const std::string& Ftp::Response::GetMessage() const
{
@ -118,23 +93,19 @@ const std::string& Ftp::Response::GetMessage() const
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Ftp::DirectoryResponse::DirectoryResponse(Ftp::Response response) :
Ftp::DirectoryResponse::DirectoryResponse(const Ftp::Response& response) :
Ftp::Response(response)
{
if (IsOk())
{
// Extract the directory from the server response
std::string::size_type begin = response.GetMessage().find('"', 0);
std::string::size_type end = response.GetMessage().find('"', begin + 1);
myDirectory = response.GetMessage().substr(begin + 1, end - begin - 1);
std::string::size_type begin = GetMessage().find('"', 0);
std::string::size_type end = GetMessage().find('"', begin + 1);
myDirectory = GetMessage().substr(begin + 1, end - begin - 1);
}
}
////////////////////////////////////////////////////////////
/// Get the directory returned in the response
////////////////////////////////////////////////////////////
const std::string& Ftp::DirectoryResponse::GetDirectory() const
{
@ -143,9 +114,7 @@ const std::string& Ftp::DirectoryResponse::GetDirectory() const
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Ftp::ListingResponse::ListingResponse(Ftp::Response response, const std::vector<char>& data) :
Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::vector<char>& data) :
Ftp::Response(response)
{
if (IsOk())
@ -163,25 +132,12 @@ Ftp::Response(response)
////////////////////////////////////////////////////////////
/// Get the number of filenames in the listing
////////////////////////////////////////////////////////////
std::size_t Ftp::ListingResponse::GetCount() const
const std::vector<std::string>& Ftp::ListingResponse::GetFilenames() const
{
return myFilenames.size();
return myFilenames;
}
////////////////////////////////////////////////////////////
/// Get the Index-th filename in the directory
////////////////////////////////////////////////////////////
const std::string& Ftp::ListingResponse::GetFilename(std::size_t index) const
{
return myFilenames[index];
}
////////////////////////////////////////////////////////////
/// Destructor -- close the connection with the server
////////////////////////////////////////////////////////////
Ftp::~Ftp()
{
@ -189,13 +145,11 @@ Ftp::~Ftp()
}
////////////////////////////////////////////////////////////
/// Connect to the specified FTP server
////////////////////////////////////////////////////////////
Ftp::Response Ftp::Connect(const IpAddress& server, unsigned short port, float timeout)
{
// Connect to the server
if (myCommandSocket.Connect(port, server, timeout) != Socket::Done)
if (myCommandSocket.Connect(server, port, timeout) != Socket::Done)
return Response(Response::ConnectionFailed);
// Get the response to the connection
@ -203,8 +157,6 @@ Ftp::Response Ftp::Connect(const IpAddress& server, unsigned short port, float t
}
////////////////////////////////////////////////////////////
/// Log in using anonymous account
////////////////////////////////////////////////////////////
Ftp::Response Ftp::Login()
{
@ -212,8 +164,6 @@ Ftp::Response Ftp::Login()
}
////////////////////////////////////////////////////////////
/// Log in using a username and a password
////////////////////////////////////////////////////////////
Ftp::Response Ftp::Login(const std::string& name, const std::string& password)
{
@ -225,22 +175,18 @@ Ftp::Response Ftp::Login(const std::string& name, const std::string& password)
}
////////////////////////////////////////////////////////////
/// Close the connection with FTP server
////////////////////////////////////////////////////////////
Ftp::Response Ftp::Disconnect()
{
// Send the exit command
Response response = SendCommand("QUIT");
if (response.IsOk())
myCommandSocket.Close();
myCommandSocket.Disconnect();
return response;
}
////////////////////////////////////////////////////////////
/// Send a null command just to prevent from being disconnected
////////////////////////////////////////////////////////////
Ftp::Response Ftp::KeepAlive()
{
@ -248,8 +194,6 @@ Ftp::Response Ftp::KeepAlive()
}
////////////////////////////////////////////////////////////
/// Get the current working directory
////////////////////////////////////////////////////////////
Ftp::DirectoryResponse Ftp::GetWorkingDirectory()
{
@ -257,9 +201,6 @@ Ftp::DirectoryResponse Ftp::GetWorkingDirectory()
}
////////////////////////////////////////////////////////////
/// Get the contents of the given directory
/// (subdirectories and files)
////////////////////////////////////////////////////////////
Ftp::ListingResponse Ftp::GetDirectoryListing(const std::string& directory)
{
@ -285,8 +226,6 @@ Ftp::ListingResponse Ftp::GetDirectoryListing(const std::string& directory)
}
////////////////////////////////////////////////////////////
/// Change the current working directory
////////////////////////////////////////////////////////////
Ftp::Response Ftp::ChangeDirectory(const std::string& directory)
{
@ -294,8 +233,6 @@ Ftp::Response Ftp::ChangeDirectory(const std::string& directory)
}
////////////////////////////////////////////////////////////
/// Go to the parent directory of the current one
////////////////////////////////////////////////////////////
Ftp::Response Ftp::ParentDirectory()
{
@ -304,16 +241,12 @@ Ftp::Response Ftp::ParentDirectory()
////////////////////////////////////////////////////////////
/// Create a new directory
////////////////////////////////////////////////////////////
Ftp::Response Ftp::MakeDirectory(const std::string& name)
Ftp::Response Ftp::CreateDirectory(const std::string& name)
{
return SendCommand("MKD", name);
}
////////////////////////////////////////////////////////////
/// Remove an existing directory
////////////////////////////////////////////////////////////
Ftp::Response Ftp::DeleteDirectory(const std::string& name)
{
@ -321,8 +254,6 @@ Ftp::Response Ftp::DeleteDirectory(const std::string& name)
}
////////////////////////////////////////////////////////////
/// Rename a file
////////////////////////////////////////////////////////////
Ftp::Response Ftp::RenameFile(const std::string& file, const std::string& newName)
{
@ -334,8 +265,6 @@ Ftp::Response Ftp::RenameFile(const std::string& file, const std::string& newNam
}
////////////////////////////////////////////////////////////
/// Remove an existing file
////////////////////////////////////////////////////////////
Ftp::Response Ftp::DeleteFile(const std::string& name)
{
@ -344,9 +273,7 @@ Ftp::Response Ftp::DeleteFile(const std::string& name)
////////////////////////////////////////////////////////////
/// Download a file from the server
////////////////////////////////////////////////////////////
Ftp::Response Ftp::Download(const std::string& distantFile, const std::string& destPath, TransferMode mode)
Ftp::Response Ftp::Download(const std::string& remoteFile, const std::string& localPath, TransferMode mode)
{
// Open a data channel using the given transfer mode
DataChannel data(*this);
@ -354,7 +281,7 @@ Ftp::Response Ftp::Download(const std::string& distantFile, const std::string& d
if (response.IsOk())
{
// Tell the server to start the transfer
response = SendCommand("RETR", distantFile);
response = SendCommand("RETR", remoteFile);
if (response.IsOk())
{
// Receive the file data
@ -366,13 +293,13 @@ Ftp::Response Ftp::Download(const std::string& distantFile, const std::string& d
if (response.IsOk())
{
// Extract the filename from the file path
std::string filename = distantFile;
std::string filename = remoteFile;
std::string::size_type pos = filename.find_last_of("/\\");
if (pos != std::string::npos)
filename = filename.substr(pos + 1);
// Make sure the destination path ends with a slash
std::string path = destPath;
std::string path = localPath;
if (!path.empty() && (path[path.size() - 1] != '\\') && (path[path.size() - 1] != '/'))
path += "/";
@ -392,9 +319,7 @@ Ftp::Response Ftp::Download(const std::string& distantFile, const std::string& d
////////////////////////////////////////////////////////////
/// Upload a file to the server
////////////////////////////////////////////////////////////
Ftp::Response Ftp::Upload(const std::string& localFile, const std::string& destPath, TransferMode mode)
Ftp::Response Ftp::Upload(const std::string& localFile, const std::string& remotePath, TransferMode mode)
{
// Get the contents of the file to send
std::ifstream file(localFile.c_str(), std::ios_base::binary);
@ -415,7 +340,7 @@ Ftp::Response Ftp::Upload(const std::string& localFile, const std::string& destP
filename = filename.substr(pos + 1);
// Make sure the destination path ends with a slash
std::string path = destPath;
std::string path = remotePath;
if (!path.empty() && (path[path.size() - 1] != '\\') && (path[path.size() - 1] != '/'))
path += "/";
@ -440,8 +365,6 @@ Ftp::Response Ftp::Upload(const std::string& localFile, const std::string& destP
}
////////////////////////////////////////////////////////////
/// Send a command to the FTP server
////////////////////////////////////////////////////////////
Ftp::Response Ftp::SendCommand(const std::string& command, const std::string& parameter)
{
@ -461,9 +384,6 @@ Ftp::Response Ftp::SendCommand(const std::string& command, const std::string& pa
}
////////////////////////////////////////////////////////////
/// Receive a response from the server
/// (usually after a command has been sent)
////////////////////////////////////////////////////////////
Ftp::Response Ftp::GetResponse()
{
@ -596,8 +516,6 @@ Ftp::Response Ftp::GetResponse()
}
////////////////////////////////////////////////////////////
/// Constructor
////////////////////////////////////////////////////////////
Ftp::DataChannel::DataChannel(Ftp& owner) :
myFtp(owner)
@ -606,18 +524,6 @@ myFtp(owner)
}
////////////////////////////////////////////////////////////
/// Destructor
////////////////////////////////////////////////////////////
Ftp::DataChannel::~DataChannel()
{
// Close the data socket
myDataSocket.Close();
}
////////////////////////////////////////////////////////////
/// Open the data channel using the specified mode and port
////////////////////////////////////////////////////////////
Ftp::Response Ftp::DataChannel::Open(Ftp::TransferMode mode)
{
@ -653,7 +559,7 @@ Ftp::Response Ftp::DataChannel::Open(Ftp::TransferMode mode)
static_cast<Uint8>(data[3]));
// Connect the data channel to the server
if (myDataSocket.Connect(port, address) == Socket::Done)
if (myDataSocket.Connect(address, port) == Socket::Done)
{
// Translate the transfer mode to the corresponding FTP parameter
std::string modeStr;
@ -679,8 +585,6 @@ Ftp::Response Ftp::DataChannel::Open(Ftp::TransferMode mode)
}
////////////////////////////////////////////////////////////
/// Receive data on the data channel until it is closed
////////////////////////////////////////////////////////////
void Ftp::DataChannel::Receive(std::vector<char>& data)
{
@ -694,12 +598,10 @@ void Ftp::DataChannel::Receive(std::vector<char>& data)
}
// Close the data socket
myDataSocket.Close();
myDataSocket.Disconnect();
}
////////////////////////////////////////////////////////////
/// Send data on the data channel
////////////////////////////////////////////////////////////
void Ftp::DataChannel::Send(const std::vector<char>& data)
{
@ -708,7 +610,7 @@ void Ftp::DataChannel::Send(const std::vector<char>& data)
myDataSocket.Send(&data[0], data.size());
// Close the data socket
myDataSocket.Close();
myDataSocket.Disconnect();
}
} // namespace sf

View file

@ -32,16 +32,16 @@
#include <sstream>
////////////////////////////////////////////////////////////
// Private data
////////////////////////////////////////////////////////////
namespace
{
////////////////////////////////////////////////////////////
// Convenience function to convert a string to lower case
////////////////////////////////////////////////////////////
// Convert a string to lower case
std::string ToLower(std::string str)
{
for (std::string::iterator i = str.begin(); i != str.end(); ++i)
*i = static_cast<char>(tolower(*i));
return str;
}
}
@ -50,19 +50,15 @@ namespace
namespace sf
{
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Http::Request::Request(Method method, const std::string& URI, const std::string& body)
Http::Request::Request(const std::string& uri, Method method, const std::string& body)
{
SetMethod(method);
SetURI(URI);
SetUri(uri);
SetHttpVersion(1, 0);
SetBody(body);
}
////////////////////////////////////////////////////////////
/// Set the value of a field; the field is added if it doesn't exist
////////////////////////////////////////////////////////////
void Http::Request::SetField(const std::string& field, const std::string& value)
{
@ -70,9 +66,6 @@ void Http::Request::SetField(const std::string& field, const std::string& value)
}
////////////////////////////////////////////////////////////
/// Set the request method.
/// This parameter is Get by default
////////////////////////////////////////////////////////////
void Http::Request::SetMethod(Http::Request::Method method)
{
@ -81,12 +74,9 @@ void Http::Request::SetMethod(Http::Request::Method method)
////////////////////////////////////////////////////////////
/// Set the target URI of the request.
/// This parameter is "/" by default
////////////////////////////////////////////////////////////
void Http::Request::SetURI(const std::string& URI)
void Http::Request::SetUri(const std::string& uri)
{
myURI = URI;
myURI = uri;
// Make sure it starts with a '/'
if (myURI.empty() || (myURI[0] != '/'))
@ -94,9 +84,6 @@ void Http::Request::SetURI(const std::string& URI)
}
////////////////////////////////////////////////////////////
/// Set the HTTP version of the request.
/// This parameter is 1.0 by default
////////////////////////////////////////////////////////////
void Http::Request::SetHttpVersion(unsigned int major, unsigned int minor)
{
@ -105,10 +92,6 @@ void Http::Request::SetHttpVersion(unsigned int major, unsigned int minor)
}
////////////////////////////////////////////////////////////
/// Set the body of the request. This parameter is optional and
/// makes sense only for POST requests.
/// This parameter is empty by default
////////////////////////////////////////////////////////////
void Http::Request::SetBody(const std::string& body)
{
@ -117,9 +100,7 @@ void Http::Request::SetBody(const std::string& body)
////////////////////////////////////////////////////////////
/// Get the string representation of a request header
////////////////////////////////////////////////////////////
std::string Http::Request::ToString() const
std::string Http::Request::Prepare() const
{
std::ostringstream out;
@ -153,8 +134,6 @@ std::string Http::Request::ToString() const
}
////////////////////////////////////////////////////////////
/// Check if the given field has been defined
////////////////////////////////////////////////////////////
bool Http::Request::HasField(const std::string& field) const
{
@ -162,8 +141,6 @@ bool Http::Request::HasField(const std::string& field) const
}
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Http::Response::Response() :
myStatus (ConnectionFailed),
@ -174,8 +151,6 @@ myMinorVersion(0)
}
////////////////////////////////////////////////////////////
/// Get the value of a field
////////////////////////////////////////////////////////////
const std::string& Http::Response::GetField(const std::string& field) const
{
@ -192,8 +167,6 @@ const std::string& Http::Response::GetField(const std::string& field) const
}
////////////////////////////////////////////////////////////
/// Get the header's status code
////////////////////////////////////////////////////////////
Http::Response::Status Http::Response::GetStatus() const
{
@ -201,8 +174,6 @@ Http::Response::Status Http::Response::GetStatus() const
}
////////////////////////////////////////////////////////////
/// Get the major HTTP version number of the response
////////////////////////////////////////////////////////////
unsigned int Http::Response::GetMajorHttpVersion() const
{
@ -210,8 +181,6 @@ unsigned int Http::Response::GetMajorHttpVersion() const
}
////////////////////////////////////////////////////////////
/// Get the major HTTP version number of the response
////////////////////////////////////////////////////////////
unsigned int Http::Response::GetMinorHttpVersion() const
{
@ -219,12 +188,6 @@ unsigned int Http::Response::GetMinorHttpVersion() const
}
////////////////////////////////////////////////////////////
/// Get the body of the response. The body can contain :
/// - the requested page (for GET requests)
/// - a response from the server (for POST requests)
/// - nothing (for HEAD requests)
/// - an error message (in case of an error)
////////////////////////////////////////////////////////////
const std::string& Http::Response::GetBody() const
{
@ -233,9 +196,7 @@ const std::string& Http::Response::GetBody() const
////////////////////////////////////////////////////////////
/// Construct the header from a response string
////////////////////////////////////////////////////////////
void Http::Response::FromString(const std::string& data)
void Http::Response::Parse(const std::string& data)
{
std::istringstream in(data);
@ -300,8 +261,6 @@ void Http::Response::FromString(const std::string& data)
}
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Http::Http() :
myHost(),
@ -311,8 +270,6 @@ myPort(0)
}
////////////////////////////////////////////////////////////
/// Construct the Http instance with the target host
////////////////////////////////////////////////////////////
Http::Http(const std::string& host, unsigned short port)
{
@ -320,8 +277,6 @@ Http::Http(const std::string& host, unsigned short port)
}
////////////////////////////////////////////////////////////
/// Set the target host
////////////////////////////////////////////////////////////
void Http::SetHost(const std::string& host, unsigned short port)
{
@ -354,17 +309,10 @@ void Http::SetHost(const std::string& host, unsigned short port)
}
////////////////////////////////////////////////////////////
/// Send a HTTP request and return the server's response.
/// You must be connected to a host before sending requests.
/// Any missing mandatory header field will be added with an appropriate value.
/// Warning : this function waits for the server's response and may
/// not return instantly; use a thread if you don't want to block your
/// application.
////////////////////////////////////////////////////////////
Http::Response Http::SendRequest(const Http::Request& request, float timeout)
{
// First make sure the request is valid -- add missing mandatory fields
// First make sure that the request is valid -- add missing mandatory fields
Request toSend(request);
if (!toSend.HasField("From"))
{
@ -397,10 +345,10 @@ Http::Response Http::SendRequest(const Http::Request& request, float timeout)
Response received;
// Connect the socket to the host
if (myConnection.Connect(myPort, myHost, timeout) == Socket::Done)
if (myConnection.Connect(myHost, myPort, timeout) == Socket::Done)
{
// Convert the request to string and send it through the connected socket
std::string requestStr = toSend.ToString();
std::string requestStr = toSend.Prepare();
if (!requestStr.empty())
{
@ -417,12 +365,12 @@ Http::Response Http::SendRequest(const Http::Request& request, float timeout)
}
// Build the Response object from the received data
received.FromString(receivedStr);
received.Parse(receivedStr);
}
}
// Close the connection
myConnection.Close();
myConnection.Disconnect();
}
return received;

View file

@ -27,7 +27,7 @@
////////////////////////////////////////////////////////////
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Http.hpp>
#include <SFML/Network/SocketHelper.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <string.h>
@ -135,37 +135,31 @@ IpAddress IpAddress::GetLocalAddress()
IpAddress localAddress;
// Create the socket
SocketHelper::SocketType sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == SocketHelper::InvalidSocket())
SocketHandle sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == priv::SocketImpl::InvalidSocket())
return localAddress;
// Build the host address (use a random port)
sockaddr_in sockAddr;
memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
sockAddr.sin_addr.s_addr = INADDR_LOOPBACK;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(4567);
// Connect the socket
if (connect(sock, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) == -1)
// Connect the socket to localhost on any port
sockaddr_in address = priv::SocketImpl::CreateAddress(INADDR_LOOPBACK, 0);
if (connect(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
{
SocketHelper::Close(sock);
priv::SocketImpl::Close(sock);
return localAddress;
}
// Get the local address of the socket connection
SocketHelper::LengthType size = sizeof(sockAddr);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&sockAddr), &size) == -1)
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&address), &size) == -1)
{
SocketHelper::Close(sock);
priv::SocketImpl::Close(sock);
return localAddress;
}
// Close the socket
SocketHelper::Close(sock);
priv::SocketImpl::Close(sock);
// Finally build the IP address
localAddress.myAddress = sockAddr.sin_addr.s_addr;
localAddress.myAddress = address.sin_addr.s_addr;
return localAddress;
}
@ -181,7 +175,7 @@ IpAddress IpAddress::GetPublicAddress(float timeout)
// (not very hard: the web page contains only our IP address).
Http server("www.sfml-dev.org");
Http::Request request(Http::Request::Get, "/ip-provider.php");
Http::Request request("/ip-provider.php", Http::Request::Get);
Http::Response page = server.SendRequest(request, timeout);
if (page.GetStatus() == Http::Response::Ok)
return IpAddress(page.GetBody());

View file

@ -26,7 +26,7 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketHelper.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/String.hpp>
#include <string.h>
@ -34,8 +34,6 @@
namespace sf
{
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
Packet::Packet() :
myReadPos(0),
myIsValid(true)
@ -44,8 +42,6 @@ myIsValid(true)
}
////////////////////////////////////////////////////////////
/// Virtual destructor
////////////////////////////////////////////////////////////
Packet::~Packet()
{
@ -53,8 +49,6 @@ Packet::~Packet()
}
////////////////////////////////////////////////////////////
/// Append data to the end of the packet
////////////////////////////////////////////////////////////
void Packet::Append(const void* data, std::size_t sizeInBytes)
{
@ -67,8 +61,6 @@ void Packet::Append(const void* data, std::size_t sizeInBytes)
}
////////////////////////////////////////////////////////////
/// Clear the packet data
////////////////////////////////////////////////////////////
void Packet::Clear()
{
@ -78,10 +70,6 @@ void Packet::Clear()
}
////////////////////////////////////////////////////////////
/// Get a pointer to the data contained in the packet
/// Warning : the returned pointer may be invalid after you
/// append data to the packet
////////////////////////////////////////////////////////////
const char* Packet::GetData() const
{
@ -89,8 +77,6 @@ const char* Packet::GetData() const
}
////////////////////////////////////////////////////////////
/// Get the size of the data contained in the packet
////////////////////////////////////////////////////////////
std::size_t Packet::GetDataSize() const
{
@ -98,8 +84,6 @@ std::size_t Packet::GetDataSize() const
}
////////////////////////////////////////////////////////////
/// Tell if the reading position has reached the end of the packet
////////////////////////////////////////////////////////////
bool Packet::EndOfPacket() const
{
@ -107,8 +91,6 @@ bool Packet::EndOfPacket() const
}
////////////////////////////////////////////////////////////
/// Tell if the packet is valid for reading
////////////////////////////////////////////////////////////
Packet::operator bool() const
{
@ -116,8 +98,6 @@ Packet::operator bool() const
}
////////////////////////////////////////////////////////////
/// Operator >> overloads to extract data from the packet
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(bool& data)
{
@ -127,6 +107,9 @@ Packet& Packet::operator >>(bool& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(Int8& data)
{
if (CheckSize(sizeof(data)))
@ -137,6 +120,9 @@ Packet& Packet::operator >>(Int8& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(Uint8& data)
{
if (CheckSize(sizeof(data)))
@ -147,6 +133,9 @@ Packet& Packet::operator >>(Uint8& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(Int16& data)
{
if (CheckSize(sizeof(data)))
@ -157,6 +146,9 @@ Packet& Packet::operator >>(Int16& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(Uint16& data)
{
if (CheckSize(sizeof(data)))
@ -167,6 +159,9 @@ Packet& Packet::operator >>(Uint16& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(Int32& data)
{
if (CheckSize(sizeof(data)))
@ -177,6 +172,9 @@ Packet& Packet::operator >>(Int32& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(Uint32& data)
{
if (CheckSize(sizeof(data)))
@ -187,6 +185,9 @@ Packet& Packet::operator >>(Uint32& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(float& data)
{
if (CheckSize(sizeof(data)))
@ -197,6 +198,9 @@ Packet& Packet::operator >>(float& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(double& data)
{
if (CheckSize(sizeof(data)))
@ -207,6 +211,9 @@ Packet& Packet::operator >>(double& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(char* data)
{
// First extract string length
@ -225,6 +232,9 @@ Packet& Packet::operator >>(char* data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(std::string& data)
{
// First extract string length
@ -243,6 +253,9 @@ Packet& Packet::operator >>(std::string& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(wchar_t* data)
{
// First extract string length
@ -263,6 +276,9 @@ Packet& Packet::operator >>(wchar_t* data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(std::wstring& data)
{
// First extract string length
@ -283,6 +299,9 @@ Packet& Packet::operator >>(std::wstring& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator >>(String& data)
{
// First extract the string length
@ -305,58 +324,83 @@ Packet& Packet::operator >>(String& data)
}
////////////////////////////////////////////////////////////
/// Operator << overloads to put data into the packet
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(bool data)
{
*this << static_cast<Uint8>(data);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(Int8 data)
{
Append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(Uint8 data)
{
Append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(Int16 data)
{
Int16 toWrite = htons(data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(Uint16 data)
{
Uint16 toWrite = htons(data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(Int32 data)
{
Int32 toWrite = htonl(data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(Uint32 data)
{
Uint32 toWrite = htonl(data);
Append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(float data)
{
Append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(double data)
{
Append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(const char* data)
{
// First insert string length
@ -370,6 +414,9 @@ Packet& Packet::operator <<(const char* data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(const std::string& data)
{
// First insert string length
@ -384,6 +431,9 @@ Packet& Packet::operator <<(const std::string& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(const wchar_t* data)
{
// First insert string length
@ -398,6 +448,9 @@ Packet& Packet::operator <<(const wchar_t* data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(const std::wstring& data)
{
// First insert string length
@ -413,6 +466,9 @@ Packet& Packet::operator <<(const std::wstring& data)
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator <<(const String& data)
{
// First insert the string length
@ -430,8 +486,6 @@ Packet& Packet::operator <<(const String& data)
}
////////////////////////////////////////////////////////////
/// Check if the packet can extract a given size of bytes
////////////////////////////////////////////////////////////
bool Packet::CheckSize(std::size_t size)
{
@ -442,21 +496,17 @@ bool Packet::CheckSize(std::size_t size)
////////////////////////////////////////////////////////////
/// Called before the packet is sent to the network
////////////////////////////////////////////////////////////
const char* Packet::OnSend(std::size_t& dataSize)
const char* Packet::OnSend(std::size_t& size)
{
dataSize = GetDataSize();
size = GetDataSize();
return GetData();
}
////////////////////////////////////////////////////////////
/// Called after the packet has been received from the network
////////////////////////////////////////////////////////////
void Packet::OnReceive(const char* data, std::size_t dataSize)
void Packet::OnReceive(const char* data, std::size_t size)
{
Append(data, dataSize);
Append(data, size);
}
} // namespace sf

155
src/SFML/Network/Socket.cpp Normal file
View file

@ -0,0 +1,155 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
Socket::Socket(Type type) :
myType (type),
mySocket (priv::SocketImpl::InvalidSocket()),
myIsBlocking(true)
{
}
////////////////////////////////////////////////////////////
Socket::~Socket()
{
// Close the socket before it gets destructed
Close();
}
////////////////////////////////////////////////////////////
void Socket::SetBlocking(bool blocking)
{
// Apply if the socket is already created
if (mySocket != priv::SocketImpl::InvalidSocket())
priv::SocketImpl::SetBlocking(mySocket, blocking);
myIsBlocking = blocking;
}
////////////////////////////////////////////////////////////
bool Socket::IsBlocking() const
{
return myIsBlocking;
}
////////////////////////////////////////////////////////////
SocketHandle Socket::GetHandle() const
{
return mySocket;
}
////////////////////////////////////////////////////////////
void Socket::Create()
{
// Don't create the socket if it already exists
if (mySocket == priv::SocketImpl::InvalidSocket())
{
SocketHandle handle = socket(PF_INET, myType == Tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
Create(handle);
}
}
////////////////////////////////////////////////////////////
void Socket::Create(SocketHandle handle)
{
// Don't create the socket if it already exists
if (mySocket == priv::SocketImpl::InvalidSocket())
{
// Assign the new handle
mySocket = handle;
// Set the current blocking state
SetBlocking(myIsBlocking);
// To avoid the "Address already in use" error message when trying to bind to the same port
int yes = 1;
if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to set socket option \"SO_REUSEADDR\" ; "
<< "binding to a same port may fail if too fast" << std::endl;
}
if (myType == Tcp)
{
// Disable the Nagle algorithm (ie. removes buffering of TCP packets)
if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to set socket option \"TCP_NODELAY\" ; "
<< "all your TCP packets will be buffered" << std::endl;
}
}
else
{
// Enable broadcast by default for UDP sockets
if (setsockopt(mySocket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to enable broadcast on UDP socket" << std::endl;
}
}
}
}
////////////////////////////////////////////////////////////
void Socket::Close()
{
// Close the socket
if (mySocket != priv::SocketImpl::InvalidSocket())
{
priv::SocketImpl::Close(mySocket);
mySocket = priv::SocketImpl::InvalidSocket();
}
// Reset the pending packet data
myPendingPacket = PendingPacket();
}
////////////////////////////////////////////////////////////
Socket::PendingPacket::PendingPacket() :
Size (0),
SizeReceived(0),
Data ()
{
}
} // namespace sf

View file

@ -0,0 +1,39 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <SFML/Network/Win32/SocketImpl.hpp>
#else
#include <SFML/Network/Unix/SocketImpl.hpp>
#endif

View file

@ -22,114 +22,116 @@
//
////////////////////////////////////////////////////////////
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SelectorBase.hpp>
#include <SFML/Network/SocketSelector.hpp>
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <utility>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace sf
{
////////////////////////////////////////////////////////////
/// Default constructor
struct SocketSelector::SocketSelectorImpl
{
fd_set AllSockets; ///< Set containing all the sockets handles
fd_set SocketsReady; ///< Set containing handles of the sockets that are ready
int MaxSocket; ///< Maximum socket handle
};
////////////////////////////////////////////////////////////
SelectorBase::SelectorBase() :
myMaxSocket(0)
SocketSelector::SocketSelector() :
myImpl(new SocketSelectorImpl)
{
Clear();
}
////////////////////////////////////////////////////////////
/// Add a socket to watch
////////////////////////////////////////////////////////////
void SelectorBase::Add(SocketHelper::SocketType socket)
SocketSelector::SocketSelector(const SocketSelector& copy) :
myImpl(new SocketSelectorImpl(*copy.myImpl))
{
FD_SET(socket, &mySet);
int size = static_cast<int>(socket);
if (size > myMaxSocket)
myMaxSocket = size;
}
////////////////////////////////////////////////////////////
/// Remove a socket
////////////////////////////////////////////////////////////
void SelectorBase::Remove(SocketHelper::SocketType socket)
SocketSelector::~SocketSelector()
{
FD_CLR(socket, &mySet);
delete myImpl;
}
////////////////////////////////////////////////////////////
/// Remove all sockets
////////////////////////////////////////////////////////////
void SelectorBase::Clear()
void SocketSelector::Add(Socket& socket)
{
FD_ZERO(&mySet);
FD_ZERO(&mySetReady);
FD_SET(socket.GetHandle(), &myImpl->AllSockets);
myMaxSocket = 0;
int size = static_cast<int>(socket.GetHandle());
if (size > myImpl->MaxSocket)
myImpl->MaxSocket = size;
}
////////////////////////////////////////////////////////////
/// Wait and collect sockets which are ready for reading.
/// This functions will return either when at least one socket
/// is ready, or when the given time is out
////////////////////////////////////////////////////////////
unsigned int SelectorBase::Wait(float timeout)
void SocketSelector::Remove(Socket& socket)
{
// Setup the timeout structure
FD_CLR(socket.GetHandle(), &myImpl->AllSockets);
FD_CLR(socket.GetHandle(), &myImpl->SocketsReady);
}
////////////////////////////////////////////////////////////
void SocketSelector::Clear()
{
FD_ZERO(&myImpl->AllSockets);
FD_ZERO(&myImpl->SocketsReady);
myImpl->MaxSocket = 0;
}
////////////////////////////////////////////////////////////
bool SocketSelector::Wait(float timeout)
{
// Setup the timeout
timeval time;
time.tv_sec = static_cast<long>(timeout);
time.tv_usec = (static_cast<long>(timeout * 1000) % 1000) * 1000;
// Prepare the set of sockets to return
mySetReady = mySet;
// Initialize the set that will contain the sockets that are ready
myImpl->SocketsReady = myImpl->AllSockets;
// Wait until one of the sockets is ready for reading, or timeout is reached
int nbSockets = select(myMaxSocket + 1, &mySetReady, NULL, NULL, timeout > 0 ? &time : NULL);
int count = select(myImpl->MaxSocket + 1, &myImpl->SocketsReady, NULL, NULL, timeout > 0 ? &time : NULL);
return nbSockets >= 0 ? static_cast<unsigned int>(nbSockets) : 0;
return count > 0;
}
////////////////////////////////////////////////////////////
/// After a call to Wait(), get the Index-th socket which is
/// ready for reading. The total number of sockets ready
/// is the integer returned by the previous call to Wait()
////////////////////////////////////////////////////////////
SocketHelper::SocketType SelectorBase::GetSocketReady(unsigned int index) const
bool SocketSelector::IsReady(Socket& socket) const
{
// We have to drop the const for FD_ISSET
fd_set* set = const_cast<fd_set*>(&mySetReady);
return FD_ISSET(socket.GetHandle(), &myImpl->SocketsReady) != 0;
}
// The standard FD_xxx interface doesn't define a direct access,
// so we must iterate through the whole set to find the socket that we're looking for
for (int i = 0; i < myMaxSocket + 1; ++i)
{
if (FD_ISSET(i, set))
{
// Current socket is ready, but is it the index-th one ?
if (index > 0)
{
index--;
}
else
{
return static_cast<SocketHelper::SocketType>(i);
}
}
}
// Invalid index : return an invalid socket
return SocketHelper::InvalidSocket();
////////////////////////////////////////////////////////////
SocketSelector& SocketSelector::operator =(const SocketSelector& right)
{
SocketSelector temp(right);
std::swap(myImpl, temp.myImpl);
return *this;
}
} // namespace sf

View file

@ -1,511 +0,0 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketTCP.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketHelper.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <string.h>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace sf
{
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
SocketTCP::SocketTCP()
{
Create(SocketHelper::InvalidSocket());
}
////////////////////////////////////////////////////////////
/// Change the blocking state of the socket
////////////////////////////////////////////////////////////
void SocketTCP::SetBlocking(bool blocking)
{
// Make sure our socket is valid
if (!IsValid())
Create();
SocketHelper::SetBlocking(mySocket, blocking);
myIsBlocking = blocking;
}
////////////////////////////////////////////////////////////
/// Connect to another computer on a specified port
////////////////////////////////////////////////////////////
Socket::Status SocketTCP::Connect(unsigned short port, const IpAddress& host, float timeout)
{
// Make sure our socket is valid
if (!IsValid())
Create();
// Build the host address
sockaddr_in sockAddr;
memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
sockAddr.sin_addr.s_addr = inet_addr(host.ToString().c_str());
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
if (timeout <= 0)
{
// ----- We're not using a timeout : just try to connect -----
if (connect(mySocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) == -1)
{
// Failed to connect
return SocketHelper::GetErrorStatus();
}
// Connection succeeded
return Socket::Done;
}
else
{
// ----- We're using a timeout : we'll need a few tricks to make it work -----
// Save the previous blocking state
bool blocking = myIsBlocking;
// Switch to non-blocking to enable our connection timeout
if (blocking)
SetBlocking(false);
// Try to connect to host
if (connect(mySocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) >= 0)
{
// We got instantly connected! (it may no happen a lot...)
return Socket::Done;
}
// Get the error status
Socket::Status status = SocketHelper::GetErrorStatus();
// If we were in non-blocking mode, return immediatly
if (!blocking)
return status;
// Otherwise, wait until something happens to our socket (success, timeout or error)
if (status == Socket::NotReady)
{
// Setup the selector
fd_set selector;
FD_ZERO(&selector);
FD_SET(mySocket, &selector);
// Setup the timeout
timeval time;
time.tv_sec = static_cast<long>(timeout);
time.tv_usec = (static_cast<long>(timeout * 1000) % 1000) * 1000;
// Wait for something to write on our socket (which means that the connection request has returned)
if (select(static_cast<int>(mySocket + 1), NULL, &selector, NULL, &time) > 0)
{
// At this point the connection may have been either accepted or refused.
// To know whether it's a success or a failure, we try to retrieve the name of the connected peer
SocketHelper::LengthType size = sizeof(sockAddr);
if (getpeername(mySocket, reinterpret_cast<sockaddr*>(&sockAddr), &size) != -1)
{
// Connection accepted
status = Socket::Done;
}
else
{
// Connection failed
status = SocketHelper::GetErrorStatus();
}
}
else
{
// Failed to connect before timeout is over
status = SocketHelper::GetErrorStatus();
}
}
// Switch back to blocking mode
SetBlocking(true);
return status;
}
}
////////////////////////////////////////////////////////////
/// Listen to a specified port for incoming data or connections
////////////////////////////////////////////////////////////
bool SocketTCP::Listen(unsigned short port)
{
// Make sure our socket is valid
if (!IsValid())
Create();
// Build the address
sockaddr_in sockAddr;
memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
// Bind the socket to the specified port
if (bind(mySocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) == -1)
{
// Not likely to happen, but...
Err() << "Failed to bind socket to port " << port << std::endl;
return false;
}
// Listen to the bound port
if (listen(mySocket, 0) == -1)
{
// Oops, socket is deaf
Err() << "Failed to listen to port " << port << std::endl;
return false;
}
return true;
}
////////////////////////////////////////////////////////////
/// Wait for a connection (must be listening to a port).
/// This function will block if the socket is blocking
////////////////////////////////////////////////////////////
Socket::Status SocketTCP::Accept(SocketTCP& connected, IpAddress* address)
{
// Address that will be filled with client informations
sockaddr_in clientAddress;
SocketHelper::LengthType length = sizeof(clientAddress);
// Accept a new connection
connected = accept(mySocket, reinterpret_cast<sockaddr*>(&clientAddress), &length);
// Check errors
if (!connected.IsValid())
{
if (address)
*address = IpAddress();
return SocketHelper::GetErrorStatus();
}
// Fill address if requested
if (address)
*address = IpAddress(inet_ntoa(clientAddress.sin_addr));
return Socket::Done;
}
////////////////////////////////////////////////////////////
/// Send an array of bytes to the host (must be connected first)
////////////////////////////////////////////////////////////
Socket::Status SocketTCP::Send(const char* data, std::size_t sizeInBytes)
{
// First check that socket is valid
if (!IsValid())
return Socket::Error;
// Check parameters
if (data && sizeInBytes)
{
// Loop until every byte has been sent
int sent = 0;
int sizeToSend = static_cast<int>(sizeInBytes);
for (int length = 0; length < sizeToSend; length += sent)
{
// Send a chunk of data
sent = send(mySocket, data + length, sizeToSend - length, 0);
// Check if an error occured
if (sent <= 0)
return SocketHelper::GetErrorStatus();
}
return Socket::Done;
}
else
{
// Error...
Err() << "Cannot send data over the network (invalid parameters)" << std::endl;
return Socket::Error;
}
}
////////////////////////////////////////////////////////////
/// Receive an array of bytes from the host (must be connected first).
/// This function will block if the socket is blocking
////////////////////////////////////////////////////////////
Socket::Status SocketTCP::Receive(char* data, std::size_t maxSize, std::size_t& sizeReceived)
{
// First clear the size received
sizeReceived = 0;
// Check that socket is valid
if (!IsValid())
return Socket::Error;
// Check parameters
if (data && maxSize)
{
// Receive a chunk of bytes
int received = recv(mySocket, data, static_cast<int>(maxSize), 0);
// Check the number of bytes received
if (received > 0)
{
sizeReceived = static_cast<std::size_t>(received);
return Socket::Done;
}
else if (received == 0)
{
return Socket::Disconnected;
}
else
{
return SocketHelper::GetErrorStatus();
}
}
else
{
// Error...
Err() << "Cannot receive data from the network (invalid parameters)" << std::endl;
return Socket::Error;
}
}
////////////////////////////////////////////////////////////
/// Send a packet of data to the host (must be connected first)
////////////////////////////////////////////////////////////
Socket::Status SocketTCP::Send(Packet& packet)
{
// Get the data to send from the packet
std::size_t dataSize = 0;
const char* data = packet.OnSend(dataSize);
// Send the packet size
Uint32 packetSize = htonl(static_cast<unsigned long>(dataSize));
Send(reinterpret_cast<const char*>(&packetSize), sizeof(packetSize));
// Send the packet data
if (packetSize > 0)
{
return Send(data, dataSize);
}
else
{
return Socket::Done;
}
}
////////////////////////////////////////////////////////////
/// Receive a packet from the host (must be connected first).
/// This function will block if the socket is blocking
////////////////////////////////////////////////////////////
Socket::Status SocketTCP::Receive(Packet& packet)
{
// We start by getting the size of the incoming packet
Uint32 packetSize = 0;
std::size_t received = 0;
if (myPendingPacketSize < 0)
{
// Loop until we've received the entire size of the packet
// (even a 4 bytes variable may be received in more than one call)
while (myPendingHeaderSize < sizeof(myPendingHeader))
{
char* data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
Socket::Status status = Receive(data, sizeof(myPendingHeader) - myPendingHeaderSize, received);
myPendingHeaderSize += received;
if (status != Socket::Done)
return status;
}
packetSize = ntohl(myPendingHeader);
myPendingHeaderSize = 0;
}
else
{
// There is a pending packet : we already know its size
packetSize = myPendingPacketSize;
}
// Then loop until we receive all the packet data
char buffer[1024];
while (myPendingPacket.size() < packetSize)
{
// Receive a chunk of data
std::size_t sizeToGet = std::min(static_cast<std::size_t>(packetSize - myPendingPacket.size()), sizeof(buffer));
Socket::Status status = Receive(buffer, sizeToGet, received);
if (status != Socket::Done)
{
// We must save the size of the pending packet until we can receive its content
if (status == Socket::NotReady)
myPendingPacketSize = packetSize;
return status;
}
// Append it into the packet
if (received > 0)
{
myPendingPacket.resize(myPendingPacket.size() + received);
char* begin = &myPendingPacket[0] + myPendingPacket.size() - received;
memcpy(begin, buffer, received);
}
}
// We have received all the datas : we can copy it to the user packet, and clear our internal packet
packet.Clear();
if (!myPendingPacket.empty())
packet.OnReceive(&myPendingPacket[0], myPendingPacket.size());
myPendingPacket.clear();
myPendingPacketSize = -1;
return Socket::Done;
}
////////////////////////////////////////////////////////////
/// Close the socket
////////////////////////////////////////////////////////////
bool SocketTCP::Close()
{
if (IsValid())
{
if (!SocketHelper::Close(mySocket))
{
Err() << "Failed to close socket" << std::endl;
return false;
}
mySocket = SocketHelper::InvalidSocket();
}
myIsBlocking = true;
return true;
}
////////////////////////////////////////////////////////////
/// Check if the socket is in a valid state ; this function
/// can be called any time to check if the socket is OK
////////////////////////////////////////////////////////////
bool SocketTCP::IsValid() const
{
return mySocket != SocketHelper::InvalidSocket();
}
////////////////////////////////////////////////////////////
/// Comparison operator ==
////////////////////////////////////////////////////////////
bool SocketTCP::operator ==(const SocketTCP& other) const
{
return mySocket == other.mySocket;
}
////////////////////////////////////////////////////////////
/// Comparison operator !=
////////////////////////////////////////////////////////////
bool SocketTCP::operator !=(const SocketTCP& other) const
{
return mySocket != other.mySocket;
}
////////////////////////////////////////////////////////////
/// Comparison operator <.
/// Provided for compatibility with standard containers, as
/// comparing two sockets doesn't make much sense...
////////////////////////////////////////////////////////////
bool SocketTCP::operator <(const SocketTCP& other) const
{
return mySocket < other.mySocket;
}
////////////////////////////////////////////////////////////
/// Construct the socket from a socket descriptor
/// (for internal use only)
////////////////////////////////////////////////////////////
SocketTCP::SocketTCP(SocketHelper::SocketType descriptor)
{
Create(descriptor);
}
////////////////////////////////////////////////////////////
/// Create the socket
////////////////////////////////////////////////////////////
void SocketTCP::Create(SocketHelper::SocketType descriptor)
{
// Use the given socket descriptor, or get a new one
mySocket = descriptor ? descriptor : socket(PF_INET, SOCK_STREAM, 0);
myIsBlocking = true;
// Reset the pending packet
myPendingHeaderSize = 0;
myPendingPacket.clear();
myPendingPacketSize = -1;
// Setup default options
if (IsValid())
{
// To avoid the "Address already in use" error message when trying to bind to the same port
int yes = 1;
if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to set socket option \"SO_REUSEADDR\" ; "
<< "binding to a same port may fail if too fast" << std::endl;
}
// Disable the Nagle algorithm (ie. removes buffering of TCP packets)
if (setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to set socket option \"TCP_NODELAY\" ; "
<< "all your TCP packets will be buffered" << std::endl;
}
// Set blocking by default (should always be the case anyway)
SetBlocking(true);
}
}
} // namespace sf

View file

@ -1,433 +0,0 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketUDP.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <string.h>
namespace sf
{
////////////////////////////////////////////////////////////
/// Default constructor
////////////////////////////////////////////////////////////
SocketUDP::SocketUDP()
{
Create();
}
////////////////////////////////////////////////////////////
/// Change the blocking state of the socket
////////////////////////////////////////////////////////////
void SocketUDP::SetBlocking(bool blocking)
{
// Make sure our socket is valid
if (!IsValid())
Create();
SocketHelper::SetBlocking(mySocket, blocking);
myIsBlocking = blocking;
}
////////////////////////////////////////////////////////////
/// Bind the socket to a specific port
////////////////////////////////////////////////////////////
bool SocketUDP::Bind(unsigned short port)
{
// Check if the socket is already bound to the specified port
if (myPort != port)
{
// If the socket was previously bound to another port, we need to unbind it first
Unbind();
if (port != 0)
{
// Build an address with the specified port
sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
sockAddr.sin_addr.s_addr = INADDR_ANY;
memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
// Bind the socket to the port
if (bind(mySocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) == -1)
{
Err() << "Failed to bind the socket to port " << port << std::endl;
myPort = 0;
return false;
}
}
// Save the new port
myPort = port;
}
return true;
}
////////////////////////////////////////////////////////////
/// Unbind the socket to its previous port
////////////////////////////////////////////////////////////
bool SocketUDP::Unbind()
{
// To unbind the socket, we just recreate it
if (myPort != 0)
{
Close();
Create();
myPort = 0;
}
return true;
}
////////////////////////////////////////////////////////////
/// Send an array of bytes
////////////////////////////////////////////////////////////
Socket::Status SocketUDP::Send(const char* data, std::size_t sizeInBytes, const IpAddress& address, unsigned short port)
{
// Make sure the socket is valid
if (!IsValid())
Create();
// Check parameters
if (data && sizeInBytes)
{
// Build the target address
sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
sockAddr.sin_addr.s_addr = inet_addr(address.ToString().c_str());
memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
// Loop until every byte has been sent
int sent = 0;
int sizeToSend = static_cast<int>(sizeInBytes);
for (int length = 0; length < sizeToSend; length += sent)
{
// Send a chunk of data
sent = sendto(mySocket, data + length, sizeToSend - length, 0, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr));
// Check errors
if (sent <= 0)
return SocketHelper::GetErrorStatus();
}
return Socket::Done;
}
else
{
// Error...
Err() << "Cannot send data over the network (invalid parameters)" << std::endl;
return Socket::Error;
}
}
////////////////////////////////////////////////////////////
/// Receive an array of bytes.
/// This function will block if the socket is blocking
////////////////////////////////////////////////////////////
Socket::Status SocketUDP::Receive(char* data, std::size_t maxSize, std::size_t& sizeReceived, IpAddress& address, unsigned short& port)
{
// First clear the size received
sizeReceived = 0;
// Make sure the socket is bound to a port
if (myPort == 0)
{
Err() << "Failed to receive data ; the UDP socket first needs to be bound to a port" << std::endl;
return Socket::Error;
}
// Make sure the socket is valid
if (!IsValid())
Create();
// Check parameters
if (data && maxSize)
{
// Data that will be filled with the other computer's address
sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = 0;
sockAddr.sin_addr.s_addr = INADDR_ANY;
memset(sockAddr.sin_zero, 0, sizeof(sockAddr.sin_zero));
SocketHelper::LengthType sockAddrSize = sizeof(sockAddr);
// Receive a chunk of bytes
int received = recvfrom(mySocket, data, static_cast<int>(maxSize), 0, reinterpret_cast<sockaddr*>(&sockAddr), &sockAddrSize);
// Check the number of bytes received
if (received > 0)
{
address = IpAddress(inet_ntoa(sockAddr.sin_addr));
port = ntohs(sockAddr.sin_port);
sizeReceived = static_cast<std::size_t>(received);
return Socket::Done;
}
else
{
address = IpAddress();
port = 0;
return received == 0 ? Socket::Disconnected : SocketHelper::GetErrorStatus();
}
}
else
{
// Error...
Err() << "Cannot receive data from the network (invalid parameters)" << std::endl;
return Socket::Error;
}
}
////////////////////////////////////////////////////////////
/// Send a packet of data
////////////////////////////////////////////////////////////
Socket::Status SocketUDP::Send(Packet& packet, const IpAddress& address, unsigned short port)
{
// Get the data to send from the packet
std::size_t dataSize = 0;
const char* data = packet.OnSend(dataSize);
// Send the packet size
Uint32 packetSize = htonl(static_cast<unsigned long>(dataSize));
Send(reinterpret_cast<const char*>(&packetSize), sizeof(packetSize), address, port);
// Send the packet data
if (packetSize > 0)
{
return Send(data, dataSize, address, port);
}
else
{
return Socket::Done;
}
}
////////////////////////////////////////////////////////////
/// Receive a packet.
/// This function will block if the socket is blocking
////////////////////////////////////////////////////////////
Socket::Status SocketUDP::Receive(Packet& packet, IpAddress& address, unsigned short& port)
{
// We start by getting the size of the incoming packet
Uint32 packetSize = 0;
std::size_t received = 0;
if (myPendingPacketSize < 0)
{
// Loop until we've received the entire size of the packet
// (even a 4 bytes variable may be received in more than one call)
while (myPendingHeaderSize < sizeof(myPendingHeader))
{
char* data = reinterpret_cast<char*>(&myPendingHeader) + myPendingHeaderSize;
Socket::Status status = Receive(data, sizeof(myPendingHeader) - myPendingHeaderSize, received, address, port);
myPendingHeaderSize += received;
if (status != Socket::Done)
return status;
}
packetSize = ntohl(myPendingHeader);
myPendingHeaderSize = 0;
}
else
{
// There is a pending packet : we already know its size
packetSize = myPendingPacketSize;
}
// Use another address instance for receiving the packet data ;
// chunks of data coming from a different sender will be discarded (and lost...)
IpAddress sender;
unsigned short senderPort;
// Then loop until we receive all the packet data
char buffer[1024];
while (myPendingPacket.size() < packetSize)
{
// Receive a chunk of data
std::size_t sizeToGet = std::min(static_cast<std::size_t>(packetSize - myPendingPacket.size()), sizeof(buffer));
Socket::Status status = Receive(buffer, sizeToGet, received, sender, senderPort);
if (status != Socket::Done)
{
// We must save the size of the pending packet until we can receive its content
if (status == Socket::NotReady)
myPendingPacketSize = packetSize;
return status;
}
// Append it into the packet
if ((sender == address) && (senderPort == port) && (received > 0))
{
myPendingPacket.resize(myPendingPacket.size() + received);
char* begin = &myPendingPacket[0] + myPendingPacket.size() - received;
memcpy(begin, buffer, received);
}
}
// We have received all the datas : we can copy it to the user packet, and clear our internal packet
packet.Clear();
if (!myPendingPacket.empty())
packet.OnReceive(&myPendingPacket[0], myPendingPacket.size());
myPendingPacket.clear();
myPendingPacketSize = -1;
return Socket::Done;
}
////////////////////////////////////////////////////////////
/// Close the socket
////////////////////////////////////////////////////////////
bool SocketUDP::Close()
{
if (IsValid())
{
if (!SocketHelper::Close(mySocket))
{
Err() << "Failed to close socket" << std::endl;
return false;
}
mySocket = SocketHelper::InvalidSocket();
}
myPort = 0;
myIsBlocking = true;
return true;
}
////////////////////////////////////////////////////////////
/// Check if the socket is in a valid state ; this function
/// can be called any time to check if the socket is OK
////////////////////////////////////////////////////////////
bool SocketUDP::IsValid() const
{
return mySocket != SocketHelper::InvalidSocket();
}
////////////////////////////////////////////////////////////
/// Get the port the socket is currently bound to
////////////////////////////////////////////////////////////
unsigned short SocketUDP::GetPort() const
{
return myPort;
}
////////////////////////////////////////////////////////////
/// Comparison operator ==
////////////////////////////////////////////////////////////
bool SocketUDP::operator ==(const SocketUDP& other) const
{
return mySocket == other.mySocket;
}
////////////////////////////////////////////////////////////
/// Comparison operator !=
////////////////////////////////////////////////////////////
bool SocketUDP::operator !=(const SocketUDP& other) const
{
return mySocket != other.mySocket;
}
////////////////////////////////////////////////////////////
/// Comparison operator <.
/// Provided for compatibility with standard containers, as
/// comparing two sockets doesn't make much sense...
////////////////////////////////////////////////////////////
bool SocketUDP::operator <(const SocketUDP& other) const
{
return mySocket < other.mySocket;
}
////////////////////////////////////////////////////////////
/// Construct the socket from a socket descriptor
/// (for internal use only)
////////////////////////////////////////////////////////////
SocketUDP::SocketUDP(SocketHelper::SocketType descriptor)
{
Create(descriptor);
}
////////////////////////////////////////////////////////////
/// Create the socket
////////////////////////////////////////////////////////////
void SocketUDP::Create(SocketHelper::SocketType descriptor)
{
// Use the given socket descriptor, or get a new one
mySocket = descriptor ? descriptor : socket(PF_INET, SOCK_DGRAM, 0);
myIsBlocking = true;
// Clear the last port used
myPort = 0;
// Reset the pending packet
myPendingHeaderSize = 0;
myPendingPacket.clear();
myPendingPacketSize = -1;
// Setup default options
if (IsValid())
{
// To avoid the "Address already in use" error message when trying to bind to the same port
int yes = 1;
if (setsockopt(mySocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to set socket option \"reuse address\" ; "
<< "binding to a same port may fail if too fast" << std::endl;
}
// Enable broadcast by default
if (setsockopt(mySocket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
Err() << "Failed to enable broadcast on UDP socket" << std::endl;
}
// Set blocking by default (should always be the case anyway)
SetBlocking(true);
}
}
} // namespace sf

View file

@ -0,0 +1,97 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/TcpListener.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
TcpListener::TcpListener() :
Socket(Tcp)
{
}
////////////////////////////////////////////////////////////
Socket::Status TcpListener::Listen(unsigned short port)
{
// Create the internal socket if it doesn't exist
Create();
// Bind the socket to the specified port
sockaddr_in address = priv::SocketImpl::CreateAddress(INADDR_ANY, port);
if (bind(GetHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
{
// Not likely to happen, but...
Err() << "Failed to bind listener socket to port " << port << std::endl;
return Error;
}
// Listen to the bound port
if (listen(GetHandle(), 0) == -1)
{
// Oops, socket is deaf
Err() << "Failed to listen to port " << port << std::endl;
return Error;
}
return Done;
}
////////////////////////////////////////////////////////////
Socket::Status TcpListener::Accept(TcpSocket& socket)
{
// Make sure that we're listening
if (GetHandle() == priv::SocketImpl::InvalidSocket())
{
Err() << "Failed to accept a new connection, the socket is not listening" << std::endl;
return Error;
}
// Accept a new connection
sockaddr_in address;
priv::SocketImpl::AddrLength length = sizeof(address);
SocketHandle remote = accept(GetHandle(), reinterpret_cast<sockaddr*>(&address), &length);
// Check for errors
if (remote == priv::SocketImpl::InvalidSocket())
return priv::SocketImpl::GetErrorStatus();
// Initialize the new connected socket
socket.Close();
socket.Create(remote);
return Done;
}
} // namespace sf

View file

@ -0,0 +1,352 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace sf
{
////////////////////////////////////////////////////////////
TcpSocket::TcpSocket() :
Socket(Tcp)
{
}
////////////////////////////////////////////////////////////
unsigned short TcpSocket::GetLocalPort() const
{
if (GetHandle() != priv::SocketImpl::InvalidSocket())
{
// Retrieve informations about the local end of the socket
sockaddr_in address;
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(GetHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
IpAddress TcpSocket::GetRemoteAddress() const
{
if (GetHandle() != priv::SocketImpl::InvalidSocket())
{
// Retrieve informations about the remote end of the socket
sockaddr_in address;
priv::SocketImpl::AddrLength size = sizeof(address);
if (getpeername(GetHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return IpAddress(ntohl(address.sin_addr.s_addr));
}
}
// We failed to retrieve the address
return IpAddress::None;
}
////////////////////////////////////////////////////////////
unsigned short TcpSocket::GetRemotePort() const
{
if (GetHandle() != priv::SocketImpl::InvalidSocket())
{
// Retrieve informations about the remote end of the socket
sockaddr_in address;
priv::SocketImpl::AddrLength size = sizeof(address);
if (getpeername(GetHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::Connect(const IpAddress& remoteAddress, unsigned short remotePort, float timeout)
{
// Create the internal socket if it doesn't exist
Create();
// Create the remote address
sockaddr_in address = priv::SocketImpl::CreateAddress(remoteAddress.ToInteger(), remotePort);
if (timeout <= 0)
{
// ----- We're not using a timeout: just try to connect -----
// Connect the socket
if (connect(GetHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
return priv::SocketImpl::GetErrorStatus();
// Connection succeeded
return Done;
}
else
{
// ----- We're using a timeout: we'll need a few tricks to make it work -----
// Save the previous blocking state
bool blocking = IsBlocking();
// Switch to non-blocking to enable our connection timeout
if (blocking)
SetBlocking(false);
// Try to connect to the remote address
if (connect(GetHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) >= 0)
{
// We got instantly connected! (it may no happen a lot...)
return Done;
}
// Get the error status
Status status = priv::SocketImpl::GetErrorStatus();
// If we were in non-blocking mode, return immediatly
if (!blocking)
return status;
// Otherwise, wait until something happens to our socket (success, timeout or error)
if (status == Socket::NotReady)
{
// Setup the selector
fd_set selector;
FD_ZERO(&selector);
FD_SET(GetHandle(), &selector);
// Setup the timeout
timeval time;
time.tv_sec = static_cast<long>(timeout);
time.tv_usec = (static_cast<long>(timeout * 1000) % 1000) * 1000;
// Wait for something to write on our socket (which means that the connection request has returned)
if (select(static_cast<int>(GetHandle() + 1), NULL, &selector, NULL, &time) > 0)
{
// At this point the connection may have been either accepted or refused.
// To know whether it's a success or a failure, we must check the address of the connected peer
if (GetRemoteAddress() != sf::IpAddress::None)
{
// Connection accepted
status = Done;
}
else
{
// Connection refused
status = priv::SocketImpl::GetErrorStatus();
}
}
else
{
// Failed to connect before timeout is over
status = priv::SocketImpl::GetErrorStatus();
}
}
// Switch back to blocking mode
SetBlocking(true);
return status;
}
}
////////////////////////////////////////////////////////////
void TcpSocket::Disconnect()
{
// Simply close the socket
Close();
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::Send(const char* data, std::size_t size)
{
// Check the parameters
if (!data || (size == 0))
{
Err() << "Cannot send data over the network (invalid parameters)" << std::endl;
return Error;
}
// Loop until every byte has been sent
int sent = 0;
int sizeToSend = static_cast<int>(size);
for (int length = 0; length < sizeToSend; length += sent)
{
// Send a chunk of data
sent = send(GetHandle(), data + length, sizeToSend - length, 0);
// Check for errors
if (sent <= 0)
return priv::SocketImpl::GetErrorStatus();
}
return Done;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::Receive(char* data, std::size_t size, std::size_t& received)
{
// First clear the variables to fill
received = 0;
// Check the parameters
if (!data || (size == 0))
{
Err() << "Cannot receive data from the network (invalid parameters)" << std::endl;
return Error;
}
// Receive a chunk of bytes
int sizeReceived = recv(GetHandle(), data, static_cast<int>(size), 0);
// Check the number of bytes received
if (sizeReceived > 0)
{
received = static_cast<std::size_t>(sizeReceived);
return Done;
}
else if (sizeReceived == 0)
{
return Socket::Disconnected;
}
else
{
return priv::SocketImpl::GetErrorStatus();
}
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::Send(Packet& packet)
{
// Get the data to send from the packet
std::size_t size = 0;
const char* data = packet.OnSend(size);
// First send the packet size
Uint32 packetSize = htonl(static_cast<unsigned long>(size));
Status status = Send(reinterpret_cast<const char*>(&packetSize), sizeof(packetSize));
// Make sure that the size was properly sent
if (status != Done)
return status;
// Send the packet data
if (packetSize > 0)
{
return Send(data, size);
}
else
{
return Done;
}
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::Receive(Packet& packet)
{
// First clear the variables to fill
packet.Clear();
// We start by getting the size of the incoming packet
Uint32 packetSize = 0;
std::size_t received = 0;
if (myPendingPacket.SizeReceived < sizeof(myPendingPacket.Size))
{
// Loop until we've received the entire size of the packet
// (even a 4 bytes variable may be received in more than one call)
while (myPendingPacket.SizeReceived < sizeof(myPendingPacket.Size))
{
char* data = reinterpret_cast<char*>(&myPendingPacket.Size) + myPendingPacket.SizeReceived;
Status status = Receive(data, sizeof(myPendingPacket.Size) - myPendingPacket.SizeReceived, received);
myPendingPacket.SizeReceived += received;
if (status != Done)
return status;
}
// The packet size has been fully received
packetSize = ntohl(myPendingPacket.Size);
}
else
{
// The packet size has already been received in a previous call
packetSize = ntohl(myPendingPacket.Size);
}
// Loop until we receive all the packet data
char buffer[1024];
while (myPendingPacket.Data.size() < packetSize)
{
// Receive a chunk of data
std::size_t sizeToGet = std::min(static_cast<std::size_t>(packetSize - myPendingPacket.Data.size()), sizeof(buffer));
Status status = Receive(buffer, sizeToGet, received);
if (status != Done)
return status;
// Append it into the packet
if (received > 0)
{
myPendingPacket.Data.resize(myPendingPacket.Data.size() + received);
char* begin = &myPendingPacket.Data[0] + myPendingPacket.Data.size() - received;
memcpy(begin, buffer, received);
}
}
// We have received all the packet data: we can copy it to the user packet
if (!myPendingPacket.Data.empty())
packet.OnReceive(&myPendingPacket.Data[0], myPendingPacket.Data.size());
// Clear the pending packet data
myPendingPacket = PendingPacket();
return Done;
}
} // namespace sf

View file

@ -0,0 +1,256 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/UdpSocket.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
namespace sf
{
////////////////////////////////////////////////////////////
UdpSocket::UdpSocket() :
Socket(Udp)
{
}
////////////////////////////////////////////////////////////
unsigned short UdpSocket::GetLocalPort() const
{
if (GetHandle() != priv::SocketImpl::InvalidSocket())
{
// Retrieve informations about the local end of the socket
sockaddr_in address;
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(GetHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::Bind(unsigned short port)
{
// Create the internal socket if it doesn't exist
Create();
// Bind the socket
sockaddr_in address = priv::SocketImpl::CreateAddress(INADDR_ANY, port);
if (bind(GetHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
{
Err() << "Failed to bind socket to port " << port << std::endl;
return Error;
}
return Done;
}
////////////////////////////////////////////////////////////
void UdpSocket::Unbind()
{
// Simply close the socket
Close();
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::Send(const char* data, std::size_t size, const IpAddress& remoteAddress, unsigned short remotePort)
{
// Create the internal socket if it doesn't exist
Create();
// Check the parameters
if (!data || (size == 0))
{
Err() << "Cannot send data over the network (invalid parameters)" << std::endl;
return Error;
}
// Build the target address
sockaddr_in address = priv::SocketImpl::CreateAddress(remoteAddress.ToInteger(), remotePort);
// Loop until every byte has been sent
int sent = 0;
int sizeToSend = static_cast<int>(size);
for (int length = 0; length < sizeToSend; length += sent)
{
// Send a chunk of data
sent = sendto(GetHandle(), data + length, sizeToSend - length, 0, reinterpret_cast<sockaddr*>(&address), sizeof(address));
// Check for errors
if (sent <= 0)
return priv::SocketImpl::GetErrorStatus();
}
return Done;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::Receive(char* data, std::size_t size, std::size_t& received, IpAddress& remoteAddress, unsigned short& remotePort)
{
// First clear the variables to fill
received = 0;
remoteAddress = IpAddress();
remotePort = 0;
// Check the parameters
if (!data || (size == 0))
{
Err() << "Cannot receive data from the network (invalid parameters)" << std::endl;
return Error;
}
// Data that will be filled with the other computer's address
sockaddr_in address = priv::SocketImpl::CreateAddress(INADDR_ANY, 0);
// Receive a chunk of bytes
priv::SocketImpl::AddrLength addressSize = sizeof(address);
int sizeReceived = recvfrom(GetHandle(), data, static_cast<int>(size), 0, reinterpret_cast<sockaddr*>(&address), &addressSize);
// Check for errors
if (sizeReceived <= 0)
return priv::SocketImpl::GetErrorStatus();
// Fill the sender informations
received = static_cast<std::size_t>(sizeReceived);
remoteAddress = IpAddress(ntohl(address.sin_addr.s_addr));
remotePort = ntohs(address.sin_port);
return Done;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::Send(Packet& packet, const IpAddress& remoteAddress, unsigned short remotePort)
{
// Get the data to send from the packet
std::size_t size = 0;
const char* data = packet.OnSend(size);
// First send the packet size
Uint32 packetSize = htonl(static_cast<unsigned long>(size));
Status status = Send(reinterpret_cast<const char*>(&packetSize), sizeof(packetSize), remoteAddress, remotePort);
// Make sure that the size was properly sent
if (status != Done)
return status;
// Finally send the packet data
if (packetSize > 0)
{
return Send(data, size, remoteAddress, remotePort);
}
else
{
return Done;
}
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::Receive(Packet& packet, IpAddress& remoteAddress, unsigned short& remotePort)
{
// First clear the variables to fill
packet.Clear();
remoteAddress = IpAddress();
remotePort = 0;
// We start by getting the size of the incoming packet
Uint32 packetSize = 0;
std::size_t received = 0;
if (myPendingPacket.SizeReceived < sizeof(myPendingPacket.Size))
{
// Loop until we've received the entire size of the packet
// (even a 4 bytes variable may be received in more than one call)
while (myPendingPacket.SizeReceived < sizeof(myPendingPacket.Size))
{
char* data = reinterpret_cast<char*>(&myPendingPacket.Size) + myPendingPacket.SizeReceived;
std::size_t size = sizeof(myPendingPacket.Size) - myPendingPacket.SizeReceived;
Status status = Receive(data, size, received, remoteAddress, remotePort);
myPendingPacket.SizeReceived += received;
if (status != Done)
return status;
}
// The packet size has been fully received
packetSize = ntohl(myPendingPacket.Size);
}
else
{
// The packet size has already been received in a previous call
packetSize = ntohl(myPendingPacket.Size);
}
// Use another address instance for receiving the packet data,
// chunks of data coming from a different sender will be discarded (and lost...)
IpAddress currentSender;
unsigned short currentPort;
// Loop until we receive all the packet data
char buffer[1024];
while (myPendingPacket.Data.size() < packetSize)
{
// Receive a chunk of data
std::size_t sizeToGet = std::min(static_cast<std::size_t>(packetSize - myPendingPacket.Data.size()), sizeof(buffer));
Status status = Receive(buffer, sizeToGet, received, currentSender, currentPort);
if (status != Done)
return status;
// Append it into the packet
if ((currentSender == remoteAddress) && (currentPort == remotePort) && (received > 0))
{
myPendingPacket.Data.resize(myPendingPacket.Data.size() + received);
char* begin = &myPendingPacket.Data[0] + myPendingPacket.Data.size() - received;
memcpy(begin, buffer, received);
}
}
// We have received all the packet data: we can copy it to the user packet
if (!myPendingPacket.Data.empty())
packet.OnReceive(&myPendingPacket.Data[0], myPendingPacket.Data.size());
// Clear the pending packet data
myPendingPacket = PendingPacket();
return Done;
}
} // namespace sf

View file

@ -25,35 +25,45 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketHelper.hpp>
#include <SFML/Network/Unix/SocketImpl.hpp>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// Return the value of the invalid socket
sockaddr_in SocketImpl::CreateAddress(unsigned long address, unsigned short port)
{
sockaddr_in addr;
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
return addr;
}
////////////////////////////////////////////////////////////
SocketHelper::SocketType SocketHelper::InvalidSocket()
SocketHandle SocketImpl::InvalidSocket()
{
return -1;
}
////////////////////////////////////////////////////////////
/// Close / destroy a socket
////////////////////////////////////////////////////////////
bool SocketHelper::Close(SocketHelper::SocketType socket)
void SocketImpl::Close(SocketHandle sock)
{
return close(socket) != -1;
close(sock);
}
////////////////////////////////////////////////////////////
/// Set a socket as blocking or non-blocking
////////////////////////////////////////////////////////////
void SocketHelper::SetBlocking(SocketHelper::SocketType sock, bool block)
void SocketImpl::SetBlocking(SocketHandle sock, bool block)
{
int status = fcntl(sock, F_GETFL);
if (block)
@ -64,9 +74,7 @@ void SocketHelper::SetBlocking(SocketHelper::SocketType sock, bool block)
////////////////////////////////////////////////////////////
/// Get the last socket error status
////////////////////////////////////////////////////////////
Socket::Status SocketHelper::GetErrorStatus()
Socket::Status SocketImpl::GetErrorStatus()
{
// The followings are sometimes equal to EWOULDBLOCK,
// so we have to make a special case for them in order
@ -86,4 +94,6 @@ Socket::Status SocketHelper::GetErrorStatus()
}
}
} // namespace priv
} // namespace sf

View file

@ -0,0 +1,109 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef SFML_SOCKETIMPL_HPP
#define SFML_SOCKETIMPL_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Helper class implementing all the non-portable
/// socket stuff; this is the Unix version
///
////////////////////////////////////////////////////////////
class SocketImpl
{
public :
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
typedef socklen_t AddrLength;
////////////////////////////////////////////////////////////
/// \brief Create an internal sockaddr_in address
///
/// \param address Target address
/// \param port Target port
///
/// \return sockaddr_in ready to be used by socket functions
///
////////////////////////////////////////////////////////////
static sockaddr_in CreateAddress(unsigned long address, unsigned short port);
////////////////////////////////////////////////////////////
/// \brief Return the value of the invalid socket
///
/// \return Special value of the invalid socket
///
////////////////////////////////////////////////////////////
static SocketHandle InvalidSocket();
////////////////////////////////////////////////////////////
/// \brief Close and destroy a socket
///
/// \param sock Handle of the socket to close
///
////////////////////////////////////////////////////////////
static void Close(SocketHandle sock);
////////////////////////////////////////////////////////////
/// \brief Set a socket as blocking or non-blocking
///
/// \param sock Handle of the socket
/// \param block New blocking state of the socket
///
////////////////////////////////////////////////////////////
static void SetBlocking(SocketHandle sock, bool block);
////////////////////////////////////////////////////////////
/// Get the last socket error status
///
/// \return Status corresponding to the last socket error
///
////////////////////////////////////////////////////////////
static Socket::Status GetErrorStatus();
};
} // namespace priv
} // namespace sf
#endif // SFML_SOCKETIMPL_HPP

View file

@ -25,43 +25,51 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/SocketHelper.hpp>
#include <SFML/Network/Win32/SocketImpl.hpp>
#include <string.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// Return the value of the invalid socket
sockaddr_in SocketImpl::CreateAddress(unsigned long address, unsigned short port)
{
sockaddr_in addr;
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
return addr;
}
////////////////////////////////////////////////////////////
SocketHelper::SocketType SocketHelper::InvalidSocket()
SocketHandle SocketImpl::InvalidSocket()
{
return INVALID_SOCKET;
}
////////////////////////////////////////////////////////////
/// Close / destroy a socket
////////////////////////////////////////////////////////////
bool SocketHelper::Close(SocketHelper::SocketType socket)
void SocketImpl::Close(SocketHandle sock)
{
return closesocket(socket) != -1;
closesocket(sock);
}
////////////////////////////////////////////////////////////
/// Set a socket as blocking or non-blocking
////////////////////////////////////////////////////////////
void SocketHelper::SetBlocking(SocketHelper::SocketType socket, bool block)
void SocketImpl::SetBlocking(SocketHandle sock, bool block)
{
unsigned long blocking = block ? 0 : 1;
ioctlsocket(socket, FIONBIO, &blocking);
ioctlsocket(sock, FIONBIO, &blocking);
}
////////////////////////////////////////////////////////////
/// Get the last socket error status
////////////////////////////////////////////////////////////
Socket::Status SocketHelper::GetErrorStatus()
Socket::Status SocketImpl::GetErrorStatus()
{
switch (WSAGetLastError())
{
@ -86,7 +94,7 @@ struct SocketInitializer
SocketInitializer()
{
WSADATA init;
WSAStartup(MAKEWORD(2,2), &init);
WSAStartup(MAKEWORD(2, 2), &init);
}
~SocketInitializer()
@ -97,4 +105,6 @@ struct SocketInitializer
SocketInitializer globalInitializer;
} // namespace priv
} // namespace sf

View file

@ -0,0 +1,103 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef SFML_SOCKETIMPL_HPP
#define SFML_SOCKETIMPL_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <winsock2.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
/// \brief Helper class implementing all the non-portable
/// socket stuff; this is the Windows version
///
////////////////////////////////////////////////////////////
class SocketImpl
{
public :
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
typedef int AddrLength;
////////////////////////////////////////////////////////////
/// \brief Create an internal sockaddr_in address
///
/// \param address Target address
/// \param port Target port
///
/// \return sockaddr_in ready to be used by socket functions
///
////////////////////////////////////////////////////////////
static sockaddr_in CreateAddress(unsigned long address, unsigned short port);
////////////////////////////////////////////////////////////
/// \brief Return the value of the invalid socket
///
/// \return Special value of the invalid socket
///
////////////////////////////////////////////////////////////
static SocketHandle InvalidSocket();
////////////////////////////////////////////////////////////
/// \brief Close and destroy a socket
///
/// \param sock Handle of the socket to close
///
////////////////////////////////////////////////////////////
static void Close(SocketHandle sock);
////////////////////////////////////////////////////////////
/// \brief Set a socket as blocking or non-blocking
///
/// \param sock Handle of the socket
/// \param block New blocking state of the socket
///
////////////////////////////////////////////////////////////
static void SetBlocking(SocketHandle sock, bool block);
////////////////////////////////////////////////////////////
/// Get the last socket error status
///
/// \return Status corresponding to the last socket error
///
////////////////////////////////////////////////////////////
static Socket::Status GetErrorStatus();
};
} // namespace priv
} // namespace sf
#endif // SFML_SOCKETIMPL_HPP