Each project is now a module, makeing this project more portable to other premake projects

-added sfml network
This commit is contained in:
Joseph Aquino 2025-12-28 05:07:16 -05:00
parent 5b0dfa5b1f
commit f4ad9dc157
29 changed files with 7034 additions and 232 deletions

View File

@ -1,9 +1,16 @@
local m = {}
local scriptdir = path.getabsolute(path.getdirectory(_SCRIPT))
local ogg = require("vendor/ogg/build-ogg")
function m.generateproject(liboutdir, intdir)
project"flac" project"flac"
cppdialect"c++17" language"C" -- c++ will mangle names and sfml wont build
kind"staticLib" kind"staticLib"
targetdir (libout) targetdir (liboutdir)
staticruntime "off"
objdir(intdir) objdir(intdir)
warnings"Off"
ogg.link()
defines defines
{ {
@ -17,38 +24,43 @@ project"flac"
defines defines
{ {
"HAVE_LROUND",--fix error in lpc.c <Static declaration of 'lround' follows non-static declaration> "HAVE_LROUND",--fix error in lpc.c <Static declaration of 'lround' follows non-static declaration>
"HAVE_STDINT_H" --fix error in alloc.h <# error> "HAVE_STDINT_H", --fix error in alloc.h <# error>
} }
includedirs includedirs
{ {
"include", path.join(scriptdir, "include"),
"src/libFLAC/include", path.join(scriptdir, "src/libFLAC/include"),
"../ogg/include"
} }
files files
{ {
"src/libFLAC/**.c", path.join(scriptdir, "src/libFLAC/**.c"),
} }
removefiles removefiles
{ {
"src/libFLAC/deduplication/**" path.join(scriptdir, "src/libFLAC/deduplication/**"),
} }
filter"system:windows" filter"system:windows"
files files
{ {
"src/share/win_utf8_io/**.c" path.join(scriptdir, "src/share/win_utf8_io/**.c"),
} }
filter "configurations:Debug"
runtime "Debug"
symbols "on"
filter "configurations:Release"
runtime "Release"
optimize "Speed"
filter"" filter""
end
function m.link()
externalincludedirs
{
path.join(scriptdir, "include"),
path.join(scriptdir, "src/libFLAC/include"),
path.join(scriptdir, "../ogg/include")
}
links {"flac", "ogg"}
end
return m

View File

@ -1,11 +1,16 @@
local m = {}
local scriptdir = path.getabsolute(path.getdirectory(_SCRIPT))
function m.generateproject(liboutdir, intdir)
project"freetype" project"freetype"
kind"staticLib" kind"staticLib"
cppdialect"c++17" language"C" -- c++ will mangle names and sfml wont build
targetdir (libout) targetdir (liboutdir)
staticruntime "off"
objdir(intdir) objdir(intdir)
warnings"Off"
includedirs {"include"} includedirs {path.join(scriptdir, "include")}
defines defines
{ {
@ -17,47 +22,47 @@ project"freetype"
files files
{ {
"src/autofit/autofit.c", path.join(scriptdir, "src/autofit/autofit.c"),
"src/base/ftbase.c", path.join(scriptdir, "src/base/ftbase.c"),
"src/base/ftbbox.c", path.join(scriptdir, "src/base/ftbbox.c"),
"src/base/ftbdf.c", path.join(scriptdir, "src/base/ftbdf.c"),
"src/base/ftbitmap.c", path.join(scriptdir, "src/base/ftbitmap.c"),
"src/base/ftcid.c", path.join(scriptdir, "src/base/ftcid.c"),
"src/base/ftfstype.c", path.join(scriptdir, "src/base/ftfstype.c"),
"src/base/ftgasp.c", path.join(scriptdir, "src/base/ftgasp.c"),
"src/base/ftglyph.c", path.join(scriptdir, "src/base/ftglyph.c"),
"src/base/ftgxval.c", path.join(scriptdir, "src/base/ftgxval.c"),
"src/base/ftinit.c", path.join(scriptdir, "src/base/ftinit.c"),
"src/base/ftmm.c", path.join(scriptdir, "src/base/ftmm.c"),
"src/base/ftotval.c", path.join(scriptdir, "src/base/ftotval.c"),
"src/base/ftpatent.c", path.join(scriptdir, "src/base/ftpatent.c"),
"src/base/ftpfr.c", path.join(scriptdir, "src/base/ftpfr.c"),
"src/base/ftstroke.c", path.join(scriptdir, "src/base/ftstroke.c"),
"src/base/ftsynth.c", path.join(scriptdir, "src/base/ftsynth.c"),
"src/base/fttype1.c", path.join(scriptdir, "src/base/fttype1.c"),
"src/base/ftwinfnt.c", path.join(scriptdir, "src/base/ftwinfnt.c"),
"src/bdf/bdf.c", path.join(scriptdir, "src/bdf/bdf.c"),
"src/bzip2/ftbzip2.c", path.join(scriptdir, "src/bzip2/ftbzip2.c"),
"src/cache/ftcache.c", path.join(scriptdir, "src/cache/ftcache.c"),
"src/cff/cff.c", path.join(scriptdir, "src/cff/cff.c"),
"src/cid/type1cid.c", path.join(scriptdir, "src/cid/type1cid.c"),
"src/gzip/ftgzip.c", path.join(scriptdir, "src/gzip/ftgzip.c"),
"src/lzw/ftlzw.c", path.join(scriptdir, "src/lzw/ftlzw.c"),
"src/pcf/pcf.c", path.join(scriptdir, "src/pcf/pcf.c"),
"src/pfr/pfr.c", path.join(scriptdir, "src/pfr/pfr.c"),
"src/psaux/psaux.c", path.join(scriptdir, "src/psaux/psaux.c"),
"src/pshinter/pshinter.c", path.join(scriptdir, "src/pshinter/pshinter.c"),
"src/psnames/psnames.c", path.join(scriptdir, "src/psnames/psnames.c"),
"src/raster/raster.c", path.join(scriptdir, "src/raster/raster.c"),
"src/sdf/sdf.c", path.join(scriptdir, "src/sdf/sdf.c"),
"src/sfnt/sfnt.c", path.join(scriptdir, "src/sfnt/sfnt.c"),
"src/smooth/smooth.c", path.join(scriptdir, "src/smooth/smooth.c"),
"src/svg/svg.c", path.join(scriptdir, "src/svg/svg.c"),
"src/truetype/truetype.c", path.join(scriptdir, "src/truetype/truetype.c"),
"src/type1/type1.c", path.join(scriptdir, "src/type1/type1.c"),
"src/type42/type42.c", path.join(scriptdir, "src/type42/type42.c"),
"src/winfonts/winfnt.c", path.join(scriptdir, "src/winfonts/winfnt.c"),
"src/base/ftdebug.c" --fix linking errors related to FT_THROW, etc path.join(scriptdir, "src/base/ftdebug.c"), --fix linking errors related to FT_THROW, et)c
} }
filter "system:linux" filter "system:linux"
@ -67,25 +72,25 @@ project"freetype"
"HAVE_UNISTD_H" --fix error in ftsystem.c <implicit declaration of function read> "HAVE_UNISTD_H" --fix error in ftsystem.c <implicit declaration of function read>
} }
filter""
filter"system:windows" filter"system:windows"
files files
{ {
"builds/windows/ftsystem.c", path.join(scriptdir, "builds/windows/ftsystem.c"),
"builds/windows/ftdebug.c" path.join(scriptdir, "builds/windows/ftdebug.c"),
} }
filter"system:linux" filter"system:linux"
files{"builds/unix/ftsystem.c"} files{path.join(scriptdir, "builds/unix/ftsystem.c")}
filter "configurations:Debug" filter "configurations:Debug"
runtime "Debug"
symbols "on"
filter "configurations:Release"
runtime "Release"
optimize"Speed" optimize"Speed"
filter"" filter""
end
function m.link()
externalincludedirs{path.join(scriptdir, "include")}
links {"freetype"}
end
return m

View File

@ -1,29 +1,35 @@
local m = {}
local scriptdir = path.getabsolute(path.getdirectory(_SCRIPT))
local sfml = require("vendor/sfml/build-sfml")
function m.generateproject(liboutdir, intdir)
project"imgui-sfml" project"imgui-sfml"
cppdialect"c++17" cppdialect"c++17"
kind"staticLib" kind"staticLib"
targetdir (libout) targetdir (liboutdir)
staticruntime "off"
objdir(intdir) objdir(intdir)
warnings"Off"
sfml.link()
includedirs includedirs
{ {
"../sfml/include", scriptdir,
"."
} }
files files
{ {
"imgui.cpp", path.join(scriptdir, "imgui.cpp"),
"imgui_draw.cpp", path.join(scriptdir, "imgui_draw.cpp"),
"imgui_tables.cpp", path.join(scriptdir, "imgui_tables.cpp"),
"imgui_widgets.cpp", path.join(scriptdir, "imgui_widgets.cpp"),
"imgui-SFML.cpp" path.join(scriptdir, "imgui-SFML.cpp"),
} }
end
filter "configurations:Debug" function m.link()
runtime "Debug" externalincludedirs{scriptdir, path.join(scriptdir, "../sfml/include")}
symbols "on" links {"imgui-sfml"}
end
filter "configurations:Release" return m
runtime "Release"
optimize "Speed"

View File

@ -1,24 +1,28 @@
project"ogg" local m = {}
cppdialect"c++17"
kind"staticLib"
targetdir (libout)
staticruntime "off"
objdir(intdir)
includedirs"include" local scriptdir = path.getabsolute(path.getdirectory(_SCRIPT))
function m.generateproject(liboutdir, intdir)
project"ogg"
language"C" -- c++ will mangle names and sfml wont build
kind"staticLib"
targetdir (liboutdir)
objdir(intdir)
warnings"Off"
includedirs {path.join(scriptdir, "include")}
files files
{ {
"src/**.h", path.join(scriptdir, "src/**.h"),
"src/**.c" path.join(scriptdir, "src/**.c"),
} }
filter "configurations:Debug" end
runtime "Debug"
symbols "on"
filter "configurations:Release" function m.link()
runtime "Release" links{"ogg"}
optimize "Speed" externalincludedirs{path.join(scriptdir, "/include")}
end
filter"" return m

View File

@ -1,11 +1,22 @@
project"sfml" local m = {}
links{"freetype", "ogg", "flac", "vorbis"} local scriptdir = path.getabsolute(path.getdirectory(_SCRIPT))
local ogg = require("vendor/ogg/build-ogg")
local flac = require("vendor/flac/build-flac")
local vorbis = require("vendor/vorbis/build-vorbis")
local freetype = require("vendor/freetype/build-freetype")
function m.generateproject(liboutdir, intdir)
project"sfml"
cppdialect"c++17" cppdialect"c++17"
kind "staticLib" kind "staticLib"
targetdir (libout) targetdir (liboutdir)
staticruntime "off"
objdir(intdir) objdir(intdir)
warnings"Off"
freetype.link()
ogg.link()
flac.link()
vorbis.link()
defines defines
{ {
@ -20,52 +31,75 @@ project"sfml"
"SFML_IS_BIG_ENDIAN=0", "SFML_IS_BIG_ENDIAN=0",
"FT2_BUILD_LIBRARY", "FT2_BUILD_LIBRARY",
"FLAC__NO_DLL", "FLAC__NO_DLL",
"OV_EXCLUDE_STATIC_CALLBACKS" "OV_EXCLUDE_STATIC_CALLBACKS",
} }
includedirs includedirs
{ {
"include", path.join(scriptdir, "include"),
"src", path.join(scriptdir, "src"),
"extlibs/headers/glad/include", path.join(scriptdir, "extlibs/headers/glad/include"),
"extlibs/headers/mingw", path.join(scriptdir, "extlibs/headers/mingw"),
"extlibs/headers/miniaudio", path.join(scriptdir, "extlibs/headers/miniaudio"),
"extlibs/headers/minimp3", path.join(scriptdir, "extlibs/headers/minimp3"),
"extlibs/headers/stb_image", path.join(scriptdir, "extlibs/headers/stb_image"),
"extlibs/headers/vulkan", path.join(scriptdir, "extlibs/headers/vulkan"),
"../freetype/include",
"../ogg/include",
"../flac/include",
"../vorbis/include"
} }
files files
{ {
"include/SFML/**.hpp", path.join(scriptdir, "include/SFML/**.hpp"),
"include/SFML/**.inl", path.join(scriptdir, "include/SFML/**.inl"),
"src/SFML/**.hpp", path.join(scriptdir, "src/SFML/**.hpp"),
"src/SFML/**.cpp" path.join(scriptdir, "src/SFML/**.cpp"),
} }
filter"system:windows" filter"system:windows"
removefiles removefiles
{ {
"src/SFML/System/Unix/**", path.join(scriptdir, "src/SFML/System/Unix/**"),
"src/SFML/Window/Unix/**" path.join(scriptdir, "src/SFML/Window/Unix/**"),
path.join(scriptdir, "src/SFML/Network/Unix/**"),
} }
filter"system:linux" filter"system:linux"
removefiles removefiles
{ {
"src/SFML/System/Win32/**", path.join(scriptdir, "src/SFML/System/Win32/**"),
"src/SFML/Window/Win32/**", path.join(scriptdir, "src/SFML/Network/Win32/**"),
"src/SFML/Main/**" path.join(scriptdir, "src/SFML/Window/Win32/**"),
path.join(scriptdir, "src/SFML/Main/**"),
} }
filter "configurations:Debug" end
runtime "Debug"
symbols "on"
filter "configurations:Release" function m.link()
runtime "Release" defines{"SFML_STATIC"}
optimize "Speed" externalincludedirs
{
path.join(scriptdir, "include"),
path.join(scriptdir, "src"),
path.join(scriptdir, "extlibs/headers/glad/include"),
path.join(scriptdir, "extlibs/headers/mingw"),
path.join(scriptdir, "extlibs/headers/miniaudio"),
path.join(scriptdir, "extlibs/headers/minimp3"),
path.join(scriptdir, "extlibs/headers/stb_image"),
path.join(scriptdir, "extlibs/headers/vulkan"),
path.join(scriptdir, "../freetype/include"),
path.join(scriptdir, "../ogg/include"),
path.join(scriptdir, "../flac/include"),
path.join(scriptdir, "../vorbis/include")
}
links{"sfml", "freetype", "flac", "vorbis", "ogg"}
filter"system:windows"
links{"legacy_stdio_definitions", "opengl32", "gdi32", "winmm", "ws2_32", "openal32"}
filter"system:linux"
links{"Xi", "Xrandr", "Xcursor", "X11", "udev", "pthread", "OpenGL", "openal"}
end
return m

View File

@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
////////////////////////////////////////////////////////////
// Portable import / export macros
////////////////////////////////////////////////////////////
#if defined(SFML_NETWORK_EXPORTS)
#define SFML_NETWORK_API SFML_API_EXPORT
#else
#define SFML_NETWORK_API SFML_API_IMPORT
#endif

View File

@ -0,0 +1,629 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp>
#include <filesystem>
#include <string>
#include <vector>
namespace sf
{
class IpAddress;
////////////////////////////////////////////////////////////
/// \brief A FTP client
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Ftp
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumeration of transfer modes
///
////////////////////////////////////////////////////////////
enum class TransferMode
{
Binary, //!< Binary mode (file is transferred as a sequence of bytes)
Ascii, //!< Text mode using ASCII encoding
Ebcdic //!< Text mode using EBCDIC encoding
};
////////////////////////////////////////////////////////////
/// \brief FTP response
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Status codes possibly returned by a FTP response
///
////////////////////////////////////////////////////////////
enum class Status
{
// 1xx: the requested action is being initiated,
// expect another reply before proceeding with a new command
RestartMarkerReply = 110, //!< Restart marker reply
ServiceReadySoon = 120, //!< Service ready in N minutes
DataConnectionAlreadyOpened = 125, //!< Data connection already opened, transfer starting
OpeningDataConnection = 150, //!< File status ok, about to open data connection
// 2xx: the requested action has been successfully completed
Ok = 200, //!< Command ok
PointlessCommand = 202, //!< Command not implemented
SystemStatus = 211, //!< System status, or system help reply
DirectoryStatus = 212, //!< Directory status
FileStatus = 213, //!< File status
HelpMessage = 214, //!< Help message
SystemType = 215, //!< NAME system type, where NAME is an official system name from the list in the Assigned Numbers document
ServiceReady = 220, //!< Service ready for new user
ClosingConnection = 221, //!< Service closing control connection
DataConnectionOpened = 225, //!< Data connection open, no transfer in progress
ClosingDataConnection = 226, //!< Closing data connection, requested file action successful
EnteringPassiveMode = 227, //!< Entering passive mode
LoggedIn = 230, //!< User logged in, proceed. Logged out if appropriate
FileActionOk = 250, //!< Requested file action ok
DirectoryOk = 257, //!< PATHNAME created
// 3xx: the command has been accepted, but the requested action
// is dormant, pending receipt of further information
NeedPassword = 331, //!< User name ok, need password
NeedAccountToLogIn = 332, //!< Need account for login
NeedInformation = 350, //!< Requested file action pending further information
// 4xx: the command was not accepted and the requested action did not take place,
// but the error condition is temporary and the action may be requested again
ServiceUnavailable = 421, //!< Service not available, closing control connection
DataConnectionUnavailable = 425, //!< Can't open data connection
TransferAborted = 426, //!< Connection closed, transfer aborted
FileActionAborted = 450, //!< Requested file action not taken
LocalError = 451, //!< Requested action aborted, local error in processing
InsufficientStorageSpace = 452, //!< Requested action not taken; insufficient storage space in system, file unavailable
// 5xx: the command was not accepted and
// the requested action did not take place
CommandUnknown = 500, //!< Syntax error, command unrecognized
ParametersUnknown = 501, //!< Syntax error in parameters or arguments
CommandNotImplemented = 502, //!< Command not implemented
BadCommandSequence = 503, //!< Bad sequence of commands
ParameterNotImplemented = 504, //!< Command not implemented for that parameter
NotLoggedIn = 530, //!< Not logged in
NeedAccountToStore = 532, //!< Need account for storing files
FileUnavailable = 550, //!< Requested action not taken, file unavailable
PageTypeUnknown = 551, //!< Requested action aborted, page type unknown
NotEnoughMemory = 552, //!< Requested file action aborted, exceeded storage allocation
FilenameNotAllowed = 553, //!< Requested action not taken, file name not allowed
// 10xx: SFML custom codes
InvalidResponse = 1000, //!< Not part of the FTP standard, generated by SFML when a received response cannot be parsed
ConnectionFailed = 1001, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection with the server fails
ConnectionClosed = 1002, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection is unexpectedly closed
InvalidFile = 1003 //!< Not part of the FTP standard, generated by SFML when a local file cannot be read or written
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor is used by the FTP client to build
/// the response.
///
/// \param code Response status code
/// \param message Response message
///
////////////////////////////////////////////////////////////
explicit Response(Status code = Status::InvalidResponse, std::string message = "");
////////////////////////////////////////////////////////////
/// \brief Check if the status code means a success
///
/// This function is defined for convenience, it is
/// equivalent to testing if the status code is < 400.
///
/// \return `true` if the status is a success, `false` if it is a failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isOk() const;
////////////////////////////////////////////////////////////
/// \brief Get the status code of the response
///
/// \return Status code
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status getStatus() const;
////////////////////////////////////////////////////////////
/// \brief Get the full message contained in the response
///
/// \return The response message
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getMessage() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Status m_status; //!< Status code returned from the server
std::string m_message; //!< Last message received from the server
};
////////////////////////////////////////////////////////////
/// \brief Specialization of FTP response returning a directory
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API DirectoryResponse : public Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param response Source response
///
////////////////////////////////////////////////////////////
DirectoryResponse(const Response& response);
////////////////////////////////////////////////////////////
/// \brief Get the directory returned in the response
///
/// \return Directory name
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::filesystem::path& getDirectory() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::filesystem::path m_directory; //!< Directory extracted from the response message
};
////////////////////////////////////////////////////////////
/// \brief Specialization of FTP response returning a
/// file name listing
////////////////////////////////////////////////////////////
class SFML_NETWORK_API ListingResponse : public Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param response Source response
/// \param data Data containing the raw listing
///
////////////////////////////////////////////////////////////
ListingResponse(const Response& response, const std::string& data);
////////////////////////////////////////////////////////////
/// \brief Return the array of directory/file names
///
/// \return Array containing the requested listing
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::vector<std::string>& getListing() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::string> m_listing; //!< Directory/file names extracted from the data
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
Ftp() = default;
////////////////////////////////////////////////////////////
/// \brief Destructor
///
/// Automatically closes the connection with the server if
/// it is still opened.
///
////////////////////////////////////////////////////////////
~Ftp();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Ftp(const Ftp&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Ftp& operator=(const Ftp&) = delete;
////////////////////////////////////////////////////////////
/// \brief Connect to the specified FTP server
///
/// The port has a default value of 21, which is the standard
/// port used by the FTP protocol. You shouldn't use a different
/// value, unless you really know what you do.
/// This function tries to connect to the server so it may take
/// a while to complete, especially if the server is not
/// reachable. To avoid blocking your application for too long,
/// you can use a timeout. The default value, `Time::Zero`, means that the
/// system timeout will be used (which is usually pretty long).
///
/// \param server Name or address of the FTP server to connect to
/// \param port Port used for the connection
/// \param timeout Maximum time to wait
///
/// \return Server response to the request
///
/// \see `disconnect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response connect(IpAddress server, unsigned short port = 21, Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Close the connection with the server
///
/// \return Server response to the request
///
/// \see `connect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response disconnect();
////////////////////////////////////////////////////////////
/// \brief Log in using an anonymous account
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response login();
////////////////////////////////////////////////////////////
/// \brief Log in using a username and a password
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \param name User name
/// \param password Password
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response login(const std::string& name, const std::string& password);
////////////////////////////////////////////////////////////
/// \brief Send a null command to keep the connection alive
///
/// This command is useful because the server may close the
/// connection automatically if no command is sent.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response keepAlive();
////////////////////////////////////////////////////////////
/// \brief Get the current working directory
///
/// The working directory is the root path for subsequent
/// operations involving directories and/or filenames.
///
/// \return Server response to the request
///
/// \see `getDirectoryListing`, `changeDirectory`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] DirectoryResponse getWorkingDirectory();
////////////////////////////////////////////////////////////
/// \brief Get the contents of the given directory
///
/// This function retrieves the sub-directories and files
/// contained in the given directory. It is not recursive.
/// The `directory` parameter is relative to the current
/// working directory.
///
/// \param directory Directory to list
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `changeDirectory`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] ListingResponse getDirectoryListing(const std::string& directory = "");
////////////////////////////////////////////////////////////
/// \brief Change the current working directory
///
/// The new directory must be relative to the current one.
///
/// \param directory New working directory
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `getDirectoryListing`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response changeDirectory(const std::string& directory);
////////////////////////////////////////////////////////////
/// \brief Go to the parent directory of the current one
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `getDirectoryListing`, `changeDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response parentDirectory();
////////////////////////////////////////////////////////////
/// \brief Create a new directory
///
/// The new directory is created as a child of the current
/// working directory.
///
/// \param name Name of the directory to create
///
/// \return Server response to the request
///
/// \see `deleteDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response createDirectory(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Remove an existing directory
///
/// The directory to remove must be relative to the
/// current working directory.
/// Use this function with caution, the directory will
/// be removed permanently!
///
/// \param name Name of the directory to remove
///
/// \return Server response to the request
///
/// \see `createDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response deleteDirectory(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Rename an existing file
///
/// The file names must be relative to the current working
/// directory.
///
/// \param file File to rename
/// \param newName New name of the file
///
/// \return Server response to the request
///
/// \see `deleteFile`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response renameFile(const std::filesystem::path& file, const std::filesystem::path& newName);
////////////////////////////////////////////////////////////
/// \brief Remove an existing file
///
/// The file name must be relative to the current working
/// directory.
/// Use this function with caution, the file will be
/// removed permanently!
///
/// \param name File to remove
///
/// \return Server response to the request
///
/// \see `renameFile`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response deleteFile(const std::filesystem::path& name);
////////////////////////////////////////////////////////////
/// \brief Download a file from the server
///
/// The file name of the distant file is relative to the
/// current working directory of the server, and the local
/// destination path is relative to the current directory
/// of your application.
/// If a file with the same file name as the distant file
/// already exists in the local destination path, it will
/// be overwritten.
///
/// \param remoteFile File name of the distant file to download
/// \param localPath The directory in which to put the file on the local computer
/// \param mode Transfer mode
///
/// \return Server response to the request
///
/// \see `upload`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response download(const std::filesystem::path& remoteFile,
const std::filesystem::path& localPath,
TransferMode mode = TransferMode::Binary);
////////////////////////////////////////////////////////////
/// \brief Upload a file to the server
///
/// The name of the local file is relative to the current
/// working directory of your application, and the
/// remote path is relative to the current directory of the
/// FTP server.
///
/// The append parameter controls whether the remote file is
/// appended to or overwritten if it already exists.
///
/// \param localFile Path of the local file to upload
/// \param remotePath The directory in which to put the file on the server
/// \param mode Transfer mode
/// \param append Pass `true` to append to or `false` to overwrite the remote file if it already exists
///
/// \return Server response to the request
///
/// \see `download`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response upload(const std::filesystem::path& localFile,
const std::filesystem::path& remotePath,
TransferMode mode = TransferMode::Binary,
bool append = false);
////////////////////////////////////////////////////////////
/// \brief Send a command to the FTP server
///
/// While the most often used commands are provided as member
/// functions in the `sf::Ftp` class, this method can be used
/// to send any FTP command to the server. If the command
/// requires one or more parameters, they can be specified
/// in `parameter`. If the server returns information, you
/// can extract it from the response using `Response::getMessage()`.
///
/// \param command Command to send
/// \param parameter Command parameter
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response sendCommand(const std::string& command, const std::string& parameter = "");
private:
////////////////////////////////////////////////////////////
/// \brief Receive a response from the server
///
/// This function must be called after each call to
/// `sendCommand` that expects a response.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
Response getResponse();
////////////////////////////////////////////////////////////
/// \brief Utility class for exchanging data with the server
/// on the data channel
///
////////////////////////////////////////////////////////////
class DataChannel;
friend class DataChannel;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
TcpSocket m_commandSocket; //!< Socket holding the control connection with the server
std::string m_receiveBuffer; //!< Received command data that is yet to be processed
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Ftp
/// \ingroup network
///
/// `sf::Ftp` is a very simple FTP client that allows you
/// to communicate with a FTP server. The FTP protocol allows
/// you to manipulate a remote file system (list files,
/// upload, download, create, remove, ...).
///
/// Using the FTP client consists of 4 parts:
/// \li Connecting to the FTP server
/// \li Logging in (either as a registered user or anonymously)
/// \li Sending commands to the server
/// \li Disconnecting (this part can be done implicitly by the destructor)
///
/// Every command returns a FTP response, which contains the
/// status code as well as a message from the server. Some
/// commands such as `getWorkingDirectory()` and `getDirectoryListing()`
/// return additional data, and use a class derived from
/// `sf::Ftp::Response` to provide this data. The most often used
/// commands are directly provided as member functions, but it is
/// also possible to use specific commands with the `sendCommand()` function.
///
/// Note that response statuses >= 1000 are not part of the FTP standard,
/// they are generated by SFML when an internal error occurs.
///
/// All commands, especially upload and download, may take some
/// time to complete. This is important to know if you don't want
/// to block your application while the server is completing
/// the task.
///
/// Usage example:
/// \code
/// // Create a new FTP client
/// sf::Ftp ftp;
///
/// // Connect to the server
/// sf::Ftp::Response response = ftp.connect("ftp://ftp.myserver.com");
/// if (response.isOk())
/// std::cout << "Connected" << std::endl;
///
/// // Log in
/// response = ftp.login("laurent", "dF6Zm89D");
/// if (response.isOk())
/// std::cout << "Logged in" << std::endl;
///
/// // Print the working directory
/// sf::Ftp::DirectoryResponse directory = ftp.getWorkingDirectory();
/// if (directory.isOk())
/// std::cout << "Working directory: " << directory.getDirectory() << std::endl;
///
/// // Create a new directory
/// response = ftp.createDirectory("files");
/// if (response.isOk())
/// std::cout << "Created new directory" << std::endl;
///
/// // Upload a file to this new directory
/// response = ftp.upload("local-path/file.txt", "files", sf::Ftp::TransferMode::Ascii);
/// if (response.isOk())
/// std::cout << "File uploaded" << std::endl;
///
/// // Send specific commands (here: FEAT to list supported FTP features)
/// response = ftp.sendCommand("FEAT");
/// if (response.isOk())
/// std::cout << "Feature list:\n" << response.getMessage() << std::endl;
///
/// // Disconnect from the server (optional)
/// ftp.disconnect();
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,480 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp>
#include <iosfwd>
#include <map>
#include <optional>
#include <string>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief A HTTP client
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Http
{
public:
////////////////////////////////////////////////////////////
/// \brief HTTP request
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Request
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumerate the available HTTP methods for a request
///
////////////////////////////////////////////////////////////
enum class Method
{
Get, //!< Request in get mode, standard method to retrieve a page
Post, //!< Request in post mode, usually to send data to a page
Head, //!< Request a page's header only
Put, //!< Request in put mode, useful for a REST API
Delete //!< Request in delete mode, useful for a REST API
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor creates a GET request, with the root
/// URI ("/") and an empty body.
///
/// \param uri Target URI
/// \param method Method to use for the request
/// \param body Content of the request's body
///
////////////////////////////////////////////////////////////
Request(const std::string& uri = "/", Method method = Method::Get, const std::string& body = "");
////////////////////////////////////////////////////////////
/// \brief Set the value of a field
///
/// The field is created if it doesn't exist. The name of
/// the field is case-insensitive.
/// By default, a request doesn't contain any field (but the
/// mandatory fields are added later by the HTTP client when
/// sending the request).
///
/// \param field Name of the field to set
/// \param value Value of the field
///
////////////////////////////////////////////////////////////
void setField(const std::string& field, const std::string& value);
////////////////////////////////////////////////////////////
/// \brief Set the request method
///
/// See the Method enumeration for a complete list of all
/// the available methods.
/// The method is `Http::Request::Method::Get` by default.
///
/// \param method Method to use for the request
///
////////////////////////////////////////////////////////////
void setMethod(Method method);
////////////////////////////////////////////////////////////
/// \brief Set the requested URI
///
/// The URI is the resource (usually a web page or a file)
/// that you want to get or post.
/// The URI is "/" (the root page) by default.
///
/// \param uri URI to request, relative to the host
///
////////////////////////////////////////////////////////////
void setUri(const std::string& uri);
////////////////////////////////////////////////////////////
/// \brief Set the HTTP version for the request
///
/// The HTTP version is 1.0 by default.
///
/// \param major Major HTTP version number
/// \param minor Minor HTTP version number
///
////////////////////////////////////////////////////////////
void setHttpVersion(unsigned int major, unsigned int minor);
////////////////////////////////////////////////////////////
/// \brief Set the body of the request
///
/// The body of a request is optional and only makes sense
/// for POST requests. It is ignored for all other methods.
/// The body is empty by default.
///
/// \param body Content of the body
///
////////////////////////////////////////////////////////////
void setBody(const std::string& body);
private:
friend class Http;
////////////////////////////////////////////////////////////
/// \brief Prepare the final request to send to the server
///
/// This is used internally by Http before sending the
/// request to the web server.
///
/// \return String containing the request, ready to be sent
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string prepare() const;
////////////////////////////////////////////////////////////
/// \brief Check if the request defines a field
///
/// This function uses case-insensitive comparisons.
///
/// \param field Name of the field to test
///
/// \return `true` if the field exists, `false` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool hasField(const std::string& field) const;
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using FieldTable = std::map<std::string, std::string>; // Use an ordered map for predictable payloads
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FieldTable m_fields; //!< Fields of the header associated to their value
Method m_method; //!< Method to use for the request
std::string m_uri; //!< Target URI of the request
unsigned int m_majorVersion{1}; //!< Major HTTP version
unsigned int m_minorVersion{}; //!< Minor HTTP version
std::string m_body; //!< Body of the request
};
////////////////////////////////////////////////////////////
/// \brief HTTP response
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumerate all the valid status codes for a response
///
////////////////////////////////////////////////////////////
enum class Status
{
// 2xx: success
Ok = 200, //!< Most common code returned when operation was successful
Created = 201, //!< The resource has successfully been created
Accepted = 202, //!< The request has been accepted, but will be processed later by the server
NoContent = 204, //!< The server didn't send any data in return
ResetContent = 205, //!< The server informs the client that it should clear the view (form) that caused the request to be sent
PartialContent = 206, //!< The server has sent a part of the resource, as a response to a partial GET request
// 3xx: redirection
MultipleChoices = 300, //!< The requested page can be accessed from several locations
MovedPermanently = 301, //!< The requested page has permanently moved to a new location
MovedTemporarily = 302, //!< The requested page has temporarily moved to a new location
NotModified = 304, //!< For conditional requests, means the requested page hasn't changed and doesn't need to be refreshed
// 4xx: client error
BadRequest = 400, //!< The server couldn't understand the request (syntax error)
Unauthorized = 401, //!< The requested page needs an authentication to be accessed
Forbidden = 403, //!< The requested page cannot be accessed at all, even with authentication
NotFound = 404, //!< The requested page doesn't exist
RangeNotSatisfiable = 407, //!< The server can't satisfy the partial GET request (with a "Range" header field)
// 5xx: server error
InternalServerError = 500, //!< The server encountered an unexpected error
NotImplemented = 501, //!< The server doesn't implement a requested feature
BadGateway = 502, //!< The gateway server has received an error from the source server
ServiceNotAvailable = 503, //!< The server is temporarily unavailable (overloaded, in maintenance, ...)
GatewayTimeout = 504, //!< The gateway server couldn't receive a response from the source server
VersionNotSupported = 505, //!< The server doesn't support the requested HTTP version
// 10xx: SFML custom codes
InvalidResponse = 1000, //!< Response is not a valid HTTP one
ConnectionFailed = 1001 //!< Connection with server failed
};
////////////////////////////////////////////////////////////
/// \brief Get the value of a field
///
/// If the field `field` is not found in the response header,
/// the empty string is returned. This function uses
/// case-insensitive comparisons.
///
/// \param field Name of the field to get
///
/// \return Value of the field, or empty string if not found
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getField(const std::string& field) const;
////////////////////////////////////////////////////////////
/// \brief Get the response status code
///
/// The status code should be the first thing to be checked
/// after receiving a response, it defines whether it is a
/// success, a failure or anything else (see the Status
/// enumeration).
///
/// \return Status code of the response
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status getStatus() const;
////////////////////////////////////////////////////////////
/// \brief Get the major HTTP version number of the response
///
/// \return Major HTTP version number
///
/// \see `getMinorHttpVersion`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned int getMajorHttpVersion() const;
////////////////////////////////////////////////////////////
/// \brief Get the minor HTTP version number of the response
///
/// \return Minor HTTP version number
///
/// \see `getMajorHttpVersion`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned int getMinorHttpVersion() const;
////////////////////////////////////////////////////////////
/// \brief Get the body of the response
///
/// The body of a response may contain:
/// \li the requested page (for GET requests)
/// \li a response from the server (for POST requests)
/// \li nothing (for HEAD requests)
/// \li an error message (in case of an error)
///
/// \return The response body
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getBody() const;
private:
friend class Http;
////////////////////////////////////////////////////////////
/// \brief Construct the header from a response string
///
/// This function is used by `Http` to build the response
/// of a request.
///
/// \param data Content of the response to parse
///
////////////////////////////////////////////////////////////
void parse(const std::string& data);
////////////////////////////////////////////////////////////
/// \brief Read values passed in the answer header
///
/// This function is used by `Http` to extract values passed
/// in the response.
///
/// \param in String stream containing the header values
///
////////////////////////////////////////////////////////////
void parseFields(std::istream& in);
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using FieldTable = std::map<std::string, std::string>; // Use an ordered map for predictable payloads
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FieldTable m_fields; //!< Fields of the header
Status m_status{Status::ConnectionFailed}; //!< Status code
unsigned int m_majorVersion{}; //!< Major HTTP version
unsigned int m_minorVersion{}; //!< Minor HTTP version
std::string m_body; //!< Body of the response
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
Http() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the HTTP client with the target host
///
/// This is equivalent to calling `setHost(host, port)`.
/// The port has a default value of 0, which means that the
/// HTTP client will use the right port according to the
/// protocol used (80 for HTTP). You should leave it like
/// this unless you really need a port other than the
/// standard one, or use an unknown protocol.
///
/// \param host Web server to connect to
/// \param port Port to use for connection
///
////////////////////////////////////////////////////////////
Http(const std::string& host, unsigned short port = 0);
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Http(const Http&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Http& operator=(const Http&) = delete;
////////////////////////////////////////////////////////////
/// \brief Set the target host
///
/// This function just stores the host address and port, it
/// doesn't actually connect to it until you send a request.
/// The port has a default value of 0, which means that the
/// HTTP client will use the right port according to the
/// protocol used (80 for HTTP). You should leave it like
/// this unless you really need a port other than the
/// standard one, or use an unknown protocol.
///
/// \param host Web server to connect to
/// \param port Port to use for connection
///
////////////////////////////////////////////////////////////
void setHost(const std::string& host, unsigned short port = 0);
////////////////////////////////////////////////////////////
/// \brief Send a HTTP request and return the server's response.
///
/// You must have a valid host before sending a request (see `setHost`).
/// Any missing mandatory header field in the request 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, or use a timeout to limit the time to wait. A value
/// of `Time::Zero` means that the client will use the system default timeout
/// (which is usually pretty long).
///
/// \param request Request to send
/// \param timeout Maximum time to wait
///
/// \return Server's response
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response sendRequest(const Request& request, Time timeout = Time::Zero);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
TcpSocket m_connection; //!< Connection to the host
std::optional<IpAddress> m_host; //!< Web host address
std::string m_hostName; //!< Web host name
unsigned short m_port{}; //!< Port used for connection with host
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Http
/// \ingroup network
///
/// `sf::Http` is a very simple HTTP client that allows you
/// to communicate with a web server. You can retrieve
/// web pages, send data to an interactive resource,
/// download a remote file, etc. The HTTPS protocol is
/// not supported.
///
/// The HTTP client is split into 3 classes:
/// \li `sf::Http::Request`
/// \li `sf::Http::Response`
/// \li `sf::Http`
///
/// `sf::Http::Request` builds the request that will be
/// sent to the server. A request is made of:
/// \li a method (what you want to do)
/// \li a target URI (usually the name of the web page or file)
/// \li one or more header fields (options that you can pass to the server)
/// \li an optional body (for POST requests)
///
/// `sf::Http::Response` parse the response from the web server
/// and provides getters to read them. The response contains:
/// \li a status code
/// \li header fields (that may be answers to the ones that you requested)
/// \li a body, which contains the contents of the requested resource
///
/// `sf::Http` provides a simple function, SendRequest, to send a
/// `sf::Http::Request` and return the corresponding `sf::Http::Response`
/// from the server.
///
/// Usage example:
/// \code
/// // Create a new HTTP client
/// sf::Http http;
///
/// // We'll work on http://www.sfml-dev.org
/// http.setHost("http://www.sfml-dev.org");
///
/// // Prepare a request to get the 'features.php' page
/// sf::Http::Request request("features.php");
///
/// // Send the request
/// sf::Http::Response response = http.sendRequest(request);
///
/// // Check the status code and display the result
/// sf::Http::Response::Status status = response.getStatus();
/// if (status == sf::Http::Response::Status::Ok)
/// {
/// std::cout << response.getBody() << std::endl;
/// }
/// else
/// {
/// std::cout << "Error " << status << std::endl;
/// }
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,297 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/System/Time.hpp>
#include <iosfwd>
#include <optional>
#include <string>
#include <string_view>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Encapsulate an IPv4 network address
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API IpAddress
{
public:
////////////////////////////////////////////////////////////
/// \brief Construct the address from a null-terminated string view
///
/// Here \a address can be either a decimal address
/// (ex: "192.168.1.56") or a network name (ex: "localhost").
///
/// \param address IP address or network name
///
/// \return Address if provided argument was valid, otherwise `std::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<IpAddress> resolve(std::string_view address);
////////////////////////////////////////////////////////////
/// \brief Construct the address from 4 bytes
///
/// Calling `IpAddress(a, b, c, d)` is equivalent to calling
/// `IpAddress::resolve("a.b.c.d")`, but safer as it doesn't
/// have to parse a string to get the address components.
///
/// \param byte0 First byte of the address
/// \param byte1 Second byte of the address
/// \param byte2 Third byte of the address
/// \param byte3 Fourth byte of the address
///
////////////////////////////////////////////////////////////
IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3);
////////////////////////////////////////////////////////////
/// \brief Construct the address from a 32-bits integer
///
/// This constructor uses the internal representation of
/// the address directly. It should be used for optimization
/// purposes, and only if you got that representation from
/// `IpAddress::toInteger()`.
///
/// \param address 4 bytes of the address packed into a 32-bits integer
///
/// \see `toInteger`
///
////////////////////////////////////////////////////////////
explicit IpAddress(std::uint32_t address);
////////////////////////////////////////////////////////////
/// \brief Get a string representation of the address
///
/// The returned string is the decimal representation of the
/// IP address (like "192.168.1.56"), even if it was constructed
/// from a host name.
///
/// \return String representation of the address
///
/// \see `toInteger`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string toString() const;
////////////////////////////////////////////////////////////
/// \brief Get an integer representation of the address
///
/// The returned number is the internal representation of the
/// address, and should be used for optimization purposes only
/// (like sending the address through a socket).
/// The integer produced by this function can then be converted
/// back to a `sf::IpAddress` with the proper constructor.
///
/// \return 32-bits unsigned integer representation of the address
///
/// \see `toString`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::uint32_t toInteger() const;
////////////////////////////////////////////////////////////
/// \brief Get the computer's local address
///
/// The local address is the address of the computer from the
/// LAN point of view, i.e. something like 192.168.1.56. It is
/// meaningful only for communications over the local network.
/// Unlike getPublicAddress, this function is fast and may be
/// used safely anywhere.
///
/// \return Local IP address of the computer on success, `std::nullopt` otherwise
///
/// \see `getPublicAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<IpAddress> getLocalAddress();
////////////////////////////////////////////////////////////
/// \brief Get the computer's public address
///
/// The public address is the address of the computer from the
/// internet point of view, i.e. something like 89.54.1.169.
/// It is necessary for communications over the world wide web.
/// The only way to get a public address is to ask it to a
/// distant website; as a consequence, this function depends on
/// both your network connection and the server, and may be
/// very slow. You should use it as few as possible. Because
/// this function depends on the network connection and on a distant
/// server, you may use a time limit if you don't want your program
/// to be possibly stuck waiting in case there is a problem; this
/// limit is deactivated by default.
///
/// \param timeout Maximum time to wait
///
/// \return Public IP address of the computer on success, `std::nullopt` otherwise
///
/// \see `getLocalAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static std::optional<IpAddress> getPublicAddress(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// NOLINTBEGIN(readability-identifier-naming)
static const IpAddress Any; //!< Value representing any address (0.0.0.0)
static const IpAddress LocalHost; //!< The "localhost" address (for connecting a computer to itself locally)
static const IpAddress Broadcast; //!< The "broadcast" address (for sending UDP messages to everyone on a local network)
// NOLINTEND(readability-identifier-naming)
private:
friend SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::uint32_t m_address; //!< Address stored as an unsigned 32 bit integer
};
////////////////////////////////////////////////////////////
/// \brief Overload of `operator==` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if both addresses are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator==(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator!=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if both addresses are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator!=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is lesser than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is greater than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator>(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if \a left is lesser or equal than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator<=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is greater or equal than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator>=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>>` to extract an IP address from an input stream
///
/// \param stream Input stream
/// \param address IP address to extract
///
/// \return Reference to the input stream
///
////////////////////////////////////////////////////////////
SFML_NETWORK_API std::istream& operator>>(std::istream& stream, std::optional<IpAddress>& address);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<<` to print an IP address to an output stream
///
/// \param stream Output stream
/// \param address IP address to print
///
/// \return Reference to the output stream
///
////////////////////////////////////////////////////////////
SFML_NETWORK_API std::ostream& operator<<(std::ostream& stream, IpAddress address);
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::IpAddress
/// \ingroup network
///
/// `sf::IpAddress` is a utility class for manipulating network
/// addresses. It provides a set a implicit constructors and
/// conversion functions to easily build or transform an IP
/// address from/to various representations.
///
/// Usage example:
/// \code
/// auto a2 = sf::IpAddress::resolve("127.0.0.1"); // the local host address
/// auto a3 = sf::IpAddress::Broadcast; // the broadcast address
/// sf::IpAddress a4(192, 168, 1, 56); // a local address
/// auto a5 = sf::IpAddress::resolve("my_computer"); // a local address created from a network name
/// auto a6 = sf::IpAddress::resolve("89.54.1.169"); // a distant address
/// auto a7 = sf::IpAddress::resolve("www.google.com"); // a distant address created from a network name
/// auto a8 = sf::IpAddress::getLocalAddress(); // my address on the local network
/// auto a9 = sf::IpAddress::getPublicAddress(); // my address on the internet
/// \endcode
///
/// Note that `sf::IpAddress` currently doesn't support IPv6
/// nor other types of network addresses.
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,548 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <string>
#include <vector>
#include <cstddef>
#include <cstdint>
namespace sf
{
class String;
////////////////////////////////////////////////////////////
/// \brief Utility class to build blocks of data to transfer
/// over the network
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Packet
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates an empty packet.
///
////////////////////////////////////////////////////////////
Packet() = default;
////////////////////////////////////////////////////////////
/// \brief Virtual destructor
///
////////////////////////////////////////////////////////////
virtual ~Packet() = default;
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
////////////////////////////////////////////////////////////
Packet(const Packet&) = default;
////////////////////////////////////////////////////////////
/// \brief Copy assignment
///
////////////////////////////////////////////////////////////
Packet& operator=(const Packet&) = default;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
Packet(Packet&&) noexcept = default;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
Packet& operator=(Packet&&) noexcept = default;
////////////////////////////////////////////////////////////
/// \brief Append data to the end of the packet
///
/// \param data Pointer to the sequence of bytes to append
/// \param sizeInBytes Number of bytes to append
///
/// \see `clear`
/// \see `getReadPosition`
///
////////////////////////////////////////////////////////////
void append(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the packet
///
/// The next read operation will read data from this position
///
/// \return The byte offset of the current read position
///
/// \see `append`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getReadPosition() const;
////////////////////////////////////////////////////////////
/// \brief Clear the packet
///
/// After calling Clear, the packet is empty.
///
/// \see `append`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Get a pointer to the data contained in the packet
///
/// Warning: the returned pointer may become invalid after
/// you append data to the packet, therefore it should never
/// be stored.
/// The return pointer is a `nullptr` if the packet is empty.
///
/// \return Pointer to the data
///
/// \see `getDataSize`
///
////////////////////////////////////////////////////////////
[[nodiscard]] const void* getData() const;
////////////////////////////////////////////////////////////
/// \brief Get the size of the data contained in the packet
///
/// This function returns the number of bytes pointed to by
/// what `getData` returns.
///
/// \return Data size, in bytes
///
/// \see `getData`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getDataSize() const;
////////////////////////////////////////////////////////////
/// \brief Tell if the reading position has reached the
/// end of the packet
///
/// This function is useful to know if there is some data
/// left to be read, without actually reading it.
///
/// \return `true` if all data was read, `false` otherwise
///
/// \see `operator` bool
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool endOfPacket() const;
////////////////////////////////////////////////////////////
/// \brief Test the validity of the packet, for reading
///
/// This operator allows to test the packet as a boolean
/// variable, to check if a reading operation was successful.
///
/// A packet will be in an invalid state if it has no more
/// data to read.
///
/// This behavior is the same as standard C++ streams.
///
/// Usage example:
/// \code
/// float x;
/// packet >> x;
/// if (packet)
/// {
/// // ok, x was extracted successfully
/// }
///
/// // -- or --
///
/// float x;
/// if (packet >> x)
/// {
/// // ok, x was extracted successfully
/// }
/// \endcode
///
/// \return `true` if last data extraction from packet was successful
///
/// \see `endOfPacket`
///
////////////////////////////////////////////////////////////
explicit operator bool() const;
////////////////////////////////////////////////////////////
/// Overload of `operator>>` to read data from the packet
///
////////////////////////////////////////////////////////////
Packet& operator>>(bool& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int8_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint8_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int16_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint16_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int32_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint32_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int64_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint64_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(float& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(double& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(char* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::string& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(wchar_t* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::wstring& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(String& data);
////////////////////////////////////////////////////////////
/// Overload of `operator<<` to write data into the packet
///
////////////////////////////////////////////////////////////
Packet& operator<<(bool data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int8_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint8_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int16_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint16_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int32_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint32_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int64_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint64_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(float data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(double data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const char* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const std::string& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const wchar_t* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const std::wstring& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const String& data);
protected:
friend class TcpSocket;
friend class UdpSocket;
////////////////////////////////////////////////////////////
/// \brief Called before the packet is sent over the network
///
/// This function can be defined by derived classes to
/// transform the data before it is sent; this can be
/// used for compression, encryption, etc.
/// The function must return a pointer to the modified data,
/// as well as the number of bytes pointed.
/// The default implementation provides the packet's data
/// without transforming it.
///
/// \param size Variable to fill with the size of data to send
///
/// \return Pointer to the array of bytes to send
///
/// \see `onReceive`
///
////////////////////////////////////////////////////////////
virtual const void* onSend(std::size_t& size);
////////////////////////////////////////////////////////////
/// \brief Called after the packet is received over the network
///
/// This function can be defined by derived classes to
/// transform the data after it is received; this can be
/// used for decompression, decryption, etc.
/// The function receives a pointer to the received data,
/// and must fill the packet with the transformed bytes.
/// The default implementation fills the packet directly
/// without transforming the data.
///
/// \param data Pointer to the received bytes
/// \param size Number of bytes
///
/// \see `onSend`
///
////////////////////////////////////////////////////////////
virtual void onReceive(const void* data, std::size_t size);
private:
////////////////////////////////////////////////////////////
/// \brief Check if the packet can extract a given number of bytes
///
/// This function updates accordingly the state of the packet.
///
/// \param size Size to check
///
/// \return `true` if \a size bytes can be read from the packet
///
////////////////////////////////////////////////////////////
bool checkSize(std::size_t size);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::byte> m_data; //!< Data stored in the packet
std::size_t m_readPos{}; //!< Current reading position in the packet
std::size_t m_sendPos{}; //!< Current send position in the packet (for handling partial sends)
bool m_isValid{true}; //!< Reading state of the packet
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Packet
/// \ingroup network
///
/// Packets provide a safe and easy way to serialize data,
/// in order to send it over the network using sockets
/// (`sf::TcpSocket`, `sf::UdpSocket`).
///
/// Packets solve 2 fundamental problems that arise when
/// transferring data over the network:
/// \li data is interpreted correctly according to the endianness
/// \li the bounds of the packet are preserved (one send == one receive)
///
/// The `sf::Packet` class provides both input and output modes.
/// It is designed to follow the behavior of standard C++ streams,
/// using operators >> and << to extract and insert data.
///
/// It is recommended to use only fixed-size types (like `std::int32_t`, etc.),
/// to avoid possible differences between the sender and the receiver.
/// Indeed, the native C++ types may have different sizes on two platforms
/// and your data may be corrupted if that happens.
///
/// Usage example:
/// \code
/// std::uint32_t x = 24;
/// std::string s = "hello";
/// double d = 5.89;
///
/// // Group the variables to send into a packet
/// sf::Packet packet;
/// packet << x << s << d;
///
/// // Send it over the network (socket is a valid sf::TcpSocket)
/// socket.send(packet);
///
/// -----------------------------------------------------------------
///
/// // Receive the packet at the other end
/// sf::Packet packet;
/// socket.receive(packet);
///
/// // Extract the variables contained in the packet
/// std::uint32_t x;
/// std::string s;
/// double d;
/// if (packet >> x >> s >> d)
/// {
/// // Data extracted successfully...
/// }
/// \endcode
///
/// Packets have built-in `operator>>` and << overloads for
/// standard types:
/// \li `bool`
/// \li fixed-size integer types (`int[8|16|32]_t`, `uint[8|16|32]_t`)
/// \li floating point numbers (`float`, `double`)
/// \li string types (`char*`, `wchar_t*`, `std::string`, `std::wstring`, `sf::String`)
///
/// Like standard streams, it is also possible to define your own
/// overloads of operators >> and << in order to handle your
/// custom types.
///
/// \code
/// struct MyStruct
/// {
/// float number{};
/// std::int8_t integer{};
/// std::string str;
/// };
///
/// sf::Packet& operator <<(sf::Packet& packet, const MyStruct& m)
/// {
/// return packet << m.number << m.integer << m.str;
/// }
///
/// sf::Packet& operator >>(sf::Packet& packet, MyStruct& m)
/// {
/// return packet >> m.number >> m.integer >> m.str;
/// }
/// \endcode
///
/// Packets also provide an extra feature that allows to apply
/// custom transformations to the data before it is sent,
/// and after it is received. This is typically used to
/// handle automatic compression or encryption of the data.
/// This is achieved by inheriting from `sf::Packet`, and overriding
/// the onSend and onReceive functions.
///
/// Here is an example:
/// \code
/// class ZipPacket : public sf::Packet
/// {
/// const void* onSend(std::size_t& size) override
/// {
/// const void* srcData = getData();
/// std::size_t srcSize = getDataSize();
///
/// return MySuperZipFunction(srcData, srcSize, &size);
/// }
///
/// void onReceive(const void* data, std::size_t size) override
/// {
/// std::size_t dstSize;
/// const void* dstData = MySuperUnzipFunction(data, size, &dstSize);
///
/// append(dstData, dstSize);
/// }
/// };
///
/// // Use like regular packets:
/// ZipPacket packet;
/// packet << x << s << d;
/// ...
/// \endcode
///
/// \see `sf::TcpSocket`, `sf::UdpSocket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,229 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/SocketHandle.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Base class for all the socket types
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Status codes that may be returned by socket functions
///
////////////////////////////////////////////////////////////
enum class Status
{
Done, //!< The socket has sent / received the data
NotReady, //!< The socket is not ready to send / receive data yet
Partial, //!< The socket sent a part of the data
Disconnected, //!< The TCP socket has been disconnected
Error //!< An unexpected error happened
};
////////////////////////////////////////////////////////////
/// \brief Some special values used by sockets
///
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static constexpr unsigned short AnyPort{0}; //!< Special value that tells the system to pick any available port
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
virtual ~Socket();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Socket(const Socket&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Socket& operator=(const Socket&) = delete;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
Socket(Socket&& socket) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
Socket& operator=(Socket&& socket) noexcept;
////////////////////////////////////////////////////////////
/// \brief Set the blocking state of the socket
///
/// In blocking mode, calls will not return until they have
/// completed their task. For example, a call to Receive in
/// blocking mode won't return until some data was actually
/// received.
/// In non-blocking mode, calls will always return immediately,
/// using the return code to signal whether there was data
/// available or not.
/// By default, all sockets are blocking.
///
/// \param blocking `true` to set the socket as blocking, `false` for non-blocking
///
/// \see `isBlocking`
///
////////////////////////////////////////////////////////////
void setBlocking(bool blocking);
////////////////////////////////////////////////////////////
/// \brief Tell whether the socket is in blocking or non-blocking mode
///
/// \return `true` if the socket is blocking, `false` otherwise
///
/// \see `setBlocking`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isBlocking() const;
protected:
////////////////////////////////////////////////////////////
/// \brief Types of protocols that the socket can use
///
////////////////////////////////////////////////////////////
enum class Type
{
Tcp, //!< TCP protocol
Udp //!< UDP protocol
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor can only be accessed by derived classes.
///
/// \param type Type of the socket (TCP or UDP)
///
////////////////////////////////////////////////////////////
explicit Socket(Type type);
////////////////////////////////////////////////////////////
/// \brief Return the internal handle of the socket
///
/// The returned handle may be invalid if the socket
/// was not created yet (or already destroyed).
/// This function can only be accessed by derived classes.
///
/// \return The internal (OS-specific) handle of the socket
///
////////////////////////////////////////////////////////////
[[nodiscard]] SocketHandle getNativeHandle() const;
////////////////////////////////////////////////////////////
/// \brief Create the internal representation of the socket
///
/// This function can only be accessed by derived classes.
///
////////////////////////////////////////////////////////////
void create();
////////////////////////////////////////////////////////////
/// \brief Create the internal representation of the socket
/// from a socket handle
///
/// This function can only be accessed by derived classes.
///
/// \param handle OS-specific handle of the socket to wrap
///
////////////////////////////////////////////////////////////
void create(SocketHandle handle);
////////////////////////////////////////////////////////////
/// \brief Close the socket gracefully
///
/// This function can only be accessed by derived classes.
///
////////////////////////////////////////////////////////////
void close();
private:
friend class SocketSelector;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Type m_type; //!< Type of the socket (TCP or UDP)
SocketHandle m_socket; //!< Socket descriptor
bool m_isBlocking{true}; //!< Current blocking mode of the socket
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::Socket
/// \ingroup network
///
/// This class mainly defines internal stuff to be used by
/// derived classes.
///
/// The only public features that it defines, and which
/// is therefore common to all the socket classes, is the
/// blocking state. All sockets can be set as blocking or
/// non-blocking.
///
/// In blocking mode, socket functions will hang until
/// the operation completes, which means that the entire
/// program (well, in fact the current thread if you use
/// multiple ones) will be stuck waiting for your socket
/// operation to complete.
///
/// In non-blocking mode, all the socket functions will
/// return immediately. If the socket is not ready to complete
/// the requested operation, the function simply returns
/// the proper status code (`Socket::Status::NotReady`).
///
/// The default mode, which is blocking, is the one that is
/// generally used, in combination with threads or selectors.
/// The non-blocking mode is rather used in real-time
/// applications that run an endless loop that can poll
/// the socket often enough, and cannot afford blocking
/// this loop.
///
/// \see `sf::TcpListener`, `sf::TcpSocket`, `sf::UdpSocket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <basetsd.h>
#endif
namespace sf
{
////////////////////////////////////////////////////////////
// Low-level socket handle type, specific to each platform
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_WINDOWS)
using SocketHandle = UINT_PTR;
#else
using SocketHandle = int;
#endif
} // namespace sf

View File

@ -0,0 +1,273 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/System/Time.hpp>
#include <memory>
namespace sf
{
class Socket;
////////////////////////////////////////////////////////////
/// \brief Multiplexer that allows to read from multiple sockets
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API SocketSelector
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
SocketSelector();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~SocketSelector();
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
/// \param copy Instance to copy
///
////////////////////////////////////////////////////////////
SocketSelector(const SocketSelector& copy);
////////////////////////////////////////////////////////////
/// \brief Overload of assignment operator
///
/// \param right Instance to assign
///
/// \return Reference to self
///
////////////////////////////////////////////////////////////
SocketSelector& operator=(const SocketSelector& right);
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
SocketSelector(SocketSelector&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
SocketSelector& operator=(SocketSelector&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Add a new socket to the selector
///
/// This function keeps a weak reference to the socket,
/// so you have to make sure that the socket is not destroyed
/// while it is stored in the selector.
/// This function does nothing if the socket is not valid.
///
/// \param socket Reference to the socket to add
///
/// \see `remove`, `clear`
///
////////////////////////////////////////////////////////////
void add(Socket& socket);
////////////////////////////////////////////////////////////
/// \brief Remove a socket from the selector
///
/// This function doesn't destroy the socket, it simply
/// removes the reference that the selector has to it.
///
/// \param socket Reference to the socket to remove
///
/// \see `add`, `clear`
///
////////////////////////////////////////////////////////////
void remove(Socket& socket);
////////////////////////////////////////////////////////////
/// \brief Remove all the sockets stored in the selector
///
/// This function doesn't destroy any instance, it simply
/// removes all the references that the selector has to
/// external sockets.
///
/// \see `add`, `remove`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Wait until one or more sockets are ready to receive
///
/// This function returns as soon as at least one socket has
/// some data available to be received. To know which sockets are
/// ready, use the `isReady` function.
/// If you use a timeout and no socket is ready before the timeout
/// is over, the function returns `false`.
///
/// \param timeout Maximum time to wait, (use Time::Zero for infinity)
///
/// \return `true` if there are sockets ready, `false` otherwise
///
/// \see `isReady`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool wait(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Test a socket to know if it is ready to receive data
///
/// This function must be used after a call to Wait, to know
/// which sockets are ready to receive data. If a socket is
/// ready, a call to receive will never block because we know
/// that there is data available to read.
/// Note that if this function returns `true` for a TcpListener,
/// this means that it is ready to accept a new connection.
///
/// \param socket Socket to test
///
/// \return `true` if the socket is ready to read, `false` otherwise
///
/// \see `isReady`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isReady(Socket& socket) const;
private:
struct SocketSelectorImpl;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::unique_ptr<SocketSelectorImpl> m_impl; //!< Opaque pointer to the implementation (which requires OS-specific types)
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::SocketSelector
/// \ingroup network
///
/// Socket selectors provide a way to wait until some data is
/// available on a set of sockets, instead of just one. This
/// is convenient when you have multiple sockets that may
/// possibly receive data, but you don't know which one will
/// be ready first. In particular, it avoids to use a thread
/// for each socket; with selectors, a single thread can handle
/// all the sockets.
///
/// All types of sockets can be used in a selector:
/// \li `sf::TcpListener`
/// \li `sf::TcpSocket`
/// \li `sf::UdpSocket`
///
/// A selector doesn't store its own copies of the sockets
/// (socket classes are not copyable anyway), it simply keeps
/// a reference to the original sockets that you pass to the
/// "add" function. Therefore, you can't use the selector as a
/// socket container, you must store them outside and make sure
/// that they are alive as long as they are used in the selector.
///
/// Using a selector is simple:
/// \li populate the selector with all the sockets that you want to observe
/// \li make it wait until there is data available on any of the sockets
/// \li test each socket to find out which ones are ready
///
/// Usage example:
/// \code
/// // Create a socket to listen to new connections
/// sf::TcpListener listener;
/// if (listener.listen(55001) != sf::Socket::Status::Done)
/// {
/// // Handle error...
/// }
///
/// // Create a list to store the future clients
/// std::vector<sf::TcpSocket> clients;
///
/// // Create a selector
/// sf::SocketSelector selector;
///
/// // Add the listener to the selector
/// selector.add(listener);
///
/// // Endless loop that waits for new connections
/// while (running)
/// {
/// // Make the selector wait for data on any socket
/// if (selector.wait())
/// {
/// // Test the listener
/// if (selector.isReady(listener))
/// {
/// // The listener is ready: there is a pending connection
/// sf::TcpSocket client;
/// if (listener.accept(client) == sf::Socket::Status::Done)
/// {
/// // Add the new client to the selector so that we will
/// // be notified when they send something
/// selector.add(client);
///
/// // Add the new client to the clients list
/// clients.push_back(std::move(client));
/// }
/// else
/// {
/// // Handle error...
/// }
/// }
/// else
/// {
/// // The listener socket is not ready, test all other sockets (the clients)
/// for (sf::TcpSocket& client : clients)
/// {
/// if (selector.isReady(client))
/// {
/// // The client has sent some data, we can receive it
/// sf::Packet packet;
/// if (client.receive(packet) == sf::Socket::Status::Done)
/// {
/// ...
/// }
/// }
/// }
/// }
/// }
/// }
/// \endcode
///
/// \see `sf::Socket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,166 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Socket.hpp>
namespace sf
{
class TcpSocket;
////////////////////////////////////////////////////////////
/// \brief Socket that listens to new TCP connections
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API TcpListener : public Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TcpListener();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not listening to a port, this function
/// returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Start listening for incoming connection attempts
///
/// This function makes the socket start listening on the
/// specified port, waiting for incoming connection attempts.
///
/// If the socket is already listening on a port when this
/// function is called, it will stop listening on the old
/// port before starting to listen on the new port.
///
/// When providing `sf::Socket::AnyPort` as port, the listener
/// will request an available port from the system.
/// The chosen port can be retrieved by calling `getLocalPort()`.
///
/// \param port Port to listen on for incoming connection attempts
/// \param address Address of the interface to listen on
///
/// \return Status code
///
/// \see `accept`, `close`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status listen(unsigned short port, IpAddress address = IpAddress::Any);
////////////////////////////////////////////////////////////
/// \brief Stop listening and close the socket
///
/// This function gracefully stops the listener. If the
/// socket is not listening, this function has no effect.
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
void close();
////////////////////////////////////////////////////////////
/// \brief Accept a new connection
///
/// If the socket is in blocking mode, this function will
/// not return until a connection is actually received.
///
/// \param socket Socket that will hold the new connection
///
/// \return Status code
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status accept(TcpSocket& socket);
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::TcpListener
/// \ingroup network
///
/// A listener socket is a special type of socket that listens to
/// a given port and waits for connections on that port.
/// This is all it can do.
///
/// When a new connection is received, you must call accept and
/// the listener returns a new instance of `sf::TcpSocket` that
/// is properly initialized and can be used to communicate with
/// the new client.
///
/// Listener sockets are specific to the TCP protocol,
/// UDP sockets are connectionless and can therefore communicate
/// directly. As a consequence, a listener socket will always
/// return the new connections as `sf::TcpSocket` instances.
///
/// A listener is automatically closed on destruction, like all
/// other types of socket. However if you want to stop listening
/// before the socket is destroyed, you can call its `close()`
/// function.
///
/// Usage example:
/// \code
/// // Create a listener socket and make it wait for new
/// // connections on port 55001
/// sf::TcpListener listener;
/// listener.listen(55001);
///
/// // Endless loop that waits for new connections
/// while (running)
/// {
/// sf::TcpSocket client;
/// if (listener.accept(client) == sf::Socket::Done)
/// {
/// // A new client just connected!
/// std::cout << "New connection received from " << client.getRemoteAddress().value() << std::endl;
/// doSomethingWith(client);
/// }
/// }
/// \endcode
///
/// \see `sf::TcpSocket`, `sf::Socket`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,317 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/Socket.hpp>
#include <SFML/System/Time.hpp>
#include <optional>
#include <vector>
#include <cstddef>
#include <cstdint>
namespace sf
{
class TcpListener;
class IpAddress;
class Packet;
////////////////////////////////////////////////////////////
/// \brief Specialized socket using the TCP protocol
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API TcpSocket : public Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TcpSocket();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not connected, this function returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `connect`, `getRemotePort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Get the address of the connected peer
///
/// If the socket is not connected, this function returns
/// an unset optional.
///
/// \return Address of the remote peer
///
/// \see `getRemotePort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::optional<IpAddress> getRemoteAddress() const;
////////////////////////////////////////////////////////////
/// \brief Get the port of the connected peer to which
/// the socket is connected
///
/// If the socket is not connected, this function returns 0.
///
/// \return Remote port to which the socket is connected
///
/// \see `getRemoteAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getRemotePort() const;
////////////////////////////////////////////////////////////
/// \brief Connect the socket to a remote peer
///
/// In blocking mode, this function may take a while, especially
/// if the remote peer is not reachable. The last parameter allows
/// you to stop trying to connect after a given timeout.
/// If the socket is already connected, the connection is
/// forcibly disconnected before attempting to connect again.
///
/// \param remoteAddress Address of the remote peer
/// \param remotePort Port of the remote peer
/// \param timeout Optional maximum time to wait
///
/// \return Status code
///
/// \see `disconnect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Disconnect the socket from its remote peer
///
/// This function gracefully closes the connection. If the
/// socket is not connected, this function has no effect.
///
/// \see `connect`
///
////////////////////////////////////////////////////////////
void disconnect();
////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer
///
/// To be able to handle partial sends over non-blocking
/// sockets, use the `send(const void*, std::size_t, std::size_t&)`
/// overload instead.
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size);
////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer
///
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
/// \param sent The number of bytes sent will be written here
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size, std::size_t& sent);
////////////////////////////////////////////////////////////
/// \brief Receive raw data from the remote peer
///
/// In blocking mode, this function will wait until some
/// bytes are actually received.
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the array to fill with the received bytes
/// \param size Maximum number of bytes that can be received
/// \param received This variable is filled with the actual number of bytes received
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data, std::size_t size, std::size_t& received);
////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to the remote peer
///
/// In non-blocking mode, if this function returns `sf::Socket::Status::Partial`,
/// you \em must retry sending the same unmodified packet before sending
/// anything else in order to guarantee the packet arrives at the remote
/// peer uncorrupted.
/// This function will fail if the socket is not connected.
///
/// \param packet Packet to send
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(Packet& packet);
////////////////////////////////////////////////////////////
/// \brief Receive a formatted packet of data from the remote peer
///
/// In blocking mode, this function will wait until the whole packet
/// has been received.
/// This function will fail if the socket is not connected.
///
/// \param packet Packet to fill with the received data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet);
private:
friend class TcpListener;
////////////////////////////////////////////////////////////
/// \brief Structure holding the data of a pending packet
///
////////////////////////////////////////////////////////////
struct PendingPacket
{
std::uint32_t size{}; //!< Data of packet size
std::size_t sizeReceived{}; //!< Number of size bytes received so far
std::vector<std::byte> data; //!< Data of the packet
};
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
PendingPacket m_pendingPacket; //!< Temporary data of the packet currently being received
std::vector<std::byte> m_blockToSendBuffer; //!< Buffer used to prepare data being sent from the socket
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::TcpSocket
/// \ingroup network
///
/// TCP is a connected protocol, which means that a TCP
/// socket can only communicate with the host it is connected
/// to. It can't send or receive anything if it is not connected.
///
/// The TCP protocol is reliable but adds a slight overhead.
/// It ensures that your data will always be received in order
/// and without errors (no data corrupted, lost or duplicated).
///
/// When a socket is connected to a remote host, you can
/// retrieve information about this host with the
/// `getRemoteAddress` and `getRemotePort` functions. You can
/// also get the local port to which the socket is bound
/// (which is automatically chosen when the socket is connected),
/// with the getLocalPort function.
///
/// Sending and receiving data can use either the low-level
/// or the high-level functions. The low-level functions
/// process a raw sequence of bytes, and cannot ensure that
/// one call to Send will exactly match one call to Receive
/// at the other end of the socket.
///
/// The high-level interface uses packets (see `sf::Packet`),
/// which are easier to use and provide more safety regarding
/// the data that is exchanged. You can look at the `sf::Packet`
/// class to get more details about how they work.
///
/// The socket is automatically disconnected when it is destroyed,
/// but if you want to explicitly close the connection while
/// the socket instance is still alive, you can call disconnect.
///
/// Usage example:
/// \code
/// // ----- The client -----
///
/// // Create a socket and connect it to 192.168.1.50 on port 55001
/// sf::TcpSocket socket;
/// socket.connect("192.168.1.50", 55001);
///
/// // Send a message to the connected host
/// std::string message = "Hi, I am a client";
/// socket.send(message.c_str(), message.size() + 1);
///
/// // Receive an answer from the server
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// socket.receive(buffer.data(), buffer.size(), received);
/// std::cout << "The server said: " << buffer.data() << std::endl;
///
/// // ----- The server -----
///
/// // Create a listener to wait for incoming connections on port 55001
/// sf::TcpListener listener;
/// listener.listen(55001);
///
/// // Wait for a connection
/// sf::TcpSocket socket;
/// listener.accept(socket);
/// std::cout << "New client connected: " << socket.getRemoteAddress().value() << std::endl;
///
/// // Receive a message from the client
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// socket.receive(buffer.data(), buffer.size(), received);
/// std::cout << "The client said: " << buffer.data() << std::endl;
///
/// // Send an answer
/// std::string message = "Welcome, client";
/// socket.send(message.c_str(), message.size() + 1);
/// \endcode
///
/// \see `sf::Socket`, `sf::UdpSocket`, `sf::Packet`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,293 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Socket.hpp>
#include <optional>
#include <vector>
#include <cstddef>
namespace sf
{
class Packet;
////////////////////////////////////////////////////////////
/// \brief Specialized socket using the UDP protocol
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API UdpSocket : public Socket
{
public:
////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static constexpr std::size_t MaxDatagramSize{65507}; //!< The maximum number of bytes that can be sent in a single UDP datagram
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
UdpSocket();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not bound to a port, this function
/// returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `bind`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Bind the socket to a specific port
///
/// Binding the socket to a port is necessary for being
/// able to receive data on that port.
///
/// When providing `sf::Socket::AnyPort` as port, the listener
/// will request an available port from the system.
/// The chosen port can be retrieved by calling `getLocalPort()`.
///
/// Since the socket can only be bound to a single port at
/// any given moment, if it is already bound when this
/// function is called, it will be unbound from the previous
/// port before being bound to the new one.
///
/// \param port Port to bind the socket to
/// \param address Address of the interface to bind to
///
/// \return Status code
///
/// \see `unbind`, `getLocalPort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status bind(unsigned short port, IpAddress address = IpAddress::Any);
////////////////////////////////////////////////////////////
/// \brief Unbind the socket from the local port to which it is bound
///
/// The port that the socket was previously bound to is immediately
/// made available to the operating system after this function is called.
/// This means that a subsequent call to `bind()` will be able to re-bind
/// the port if no other process has done so in the mean time.
/// If the socket is not bound to a port, this function has no effect.
///
/// \see `bind`
///
////////////////////////////////////////////////////////////
void unbind();
////////////////////////////////////////////////////////////
/// \brief Send raw data to a remote peer
///
/// Make sure that `size` is not greater than
/// `UdpSocket::MaxDatagramSize`, otherwise this function will
/// fail and no data will be sent.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
/// \param remoteAddress Address of the receiver
/// \param remotePort Port of the receiver to send the data to
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort);
////////////////////////////////////////////////////////////
/// \brief Receive raw data from a remote peer
///
/// In blocking mode, this function will wait until some
/// bytes are actually received.
/// Be careful to use a buffer which is large enough for
/// the data that you intend to receive, if it is too small
/// then an error will be returned and *all* the data will
/// be lost.
///
/// \param data Pointer to the array to fill with the received bytes
/// \param size Maximum number of bytes that can be received
/// \param received This variable is filled with the actual number of bytes received
/// \param remoteAddress Address of the peer that sent the data
/// \param remotePort Port of the peer that sent the data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data,
std::size_t size,
std::size_t& received,
std::optional<IpAddress>& remoteAddress,
unsigned short& remotePort);
////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to a remote peer
///
/// Make sure that the packet size is not greater than
/// `UdpSocket::MaxDatagramSize`, otherwise this function will
/// fail and no data will be sent.
///
/// \param packet Packet to send
/// \param remoteAddress Address of the receiver
/// \param remotePort Port of the receiver to send the data to
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort);
////////////////////////////////////////////////////////////
/// \brief Receive a formatted packet of data from a remote peer
///
/// In blocking mode, this function will wait until the whole packet
/// has been received.
///
/// \param packet Packet to fill with the received data
/// \param remoteAddress Address of the peer that sent the data
/// \param remotePort Port of the peer that sent the data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet, std::optional<IpAddress>& remoteAddress, unsigned short& remotePort);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::byte> m_buffer{MaxDatagramSize}; //!< Temporary buffer holding the received data in Receive(Packet)
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class sf::UdpSocket
/// \ingroup network
///
/// A UDP socket is a connectionless socket. Instead of
/// connecting once to a remote host, like TCP sockets,
/// it can send to and receive from any host at any time.
///
/// It is a datagram protocol: bounded blocks of data (datagrams)
/// are transferred over the network rather than a continuous
/// stream of data (TCP). Therefore, one call to send will always
/// match one call to receive (if the datagram is not lost),
/// with the same data that was sent.
///
/// The UDP protocol is lightweight but unreliable. Unreliable
/// means that datagrams may be duplicated, be lost or
/// arrive reordered. However, if a datagram arrives, its
/// data is guaranteed to be valid.
///
/// UDP is generally used for real-time communication
/// (audio or video streaming, real-time games, etc.) where
/// speed is crucial and lost data doesn't matter much.
///
/// Sending and receiving data can use either the low-level
/// or the high-level functions. The low-level functions
/// process a raw sequence of bytes, whereas the high-level
/// interface uses packets (see `sf::Packet`), which are easier
/// to use and provide more safety regarding the data that is
/// exchanged. You can look at the `sf::Packet` class to get
/// more details about how they work.
///
/// It is important to note that `UdpSocket` is unable to send
/// datagrams bigger than `MaxDatagramSize`. In this case, it
/// returns an error and doesn't send anything. This applies
/// to both raw data and packets. Indeed, even packets are
/// unable to split and recompose data, due to the unreliability
/// of the protocol (dropped, mixed or duplicated datagrams may
/// lead to a big mess when trying to recompose a packet).
///
/// If the socket is bound to a port, it is automatically
/// unbound from it when the socket is destroyed. However,
/// you can unbind the socket explicitly with the Unbind
/// function if necessary, to stop receiving messages or
/// make the port available for other sockets.
///
/// Usage example:
/// \code
/// // ----- The client -----
///
/// // Create a socket and bind it to the port 55001
/// sf::UdpSocket socket;
/// socket.bind(55001);
///
/// // Send a message to 192.168.1.50 on port 55002
/// std::string message = "Hi, I am " + sf::IpAddress::getLocalAddress().toString();
/// socket.send(message.c_str(), message.size() + 1, "192.168.1.50", 55002);
///
/// // Receive an answer (most likely from 192.168.1.50, but could be anyone else)
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// std::optional<sf::IpAddress> sender;
/// unsigned short port;
/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == sf::Socket::Status::Done)
/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl;
///
/// // ----- The server -----
///
/// // Create a socket and bind it to the port 55002
/// sf::UdpSocket socket;
/// socket.bind(55002);
///
/// // Receive a message from anyone
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// std::optional<sf::IpAddress> sender;
/// unsigned short port;
/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == sf::Socket::Status::Done)
/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl;
///
/// // Send an answer
/// std::string message = "Welcome " + sender.toString();
/// socket.send(message.c_str(), message.size() + 1, sender, port);
/// \endcode
///
/// \see `sf::Socket`, `sf::TcpSocket`, `sf::Packet`
///
////////////////////////////////////////////////////////////

View File

@ -0,0 +1,643 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/Ftp.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <array>
#include <fstream>
#include <ostream>
#include <sstream>
#include <utility>
#include <cctype>
#include <cstddef>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
class Ftp::DataChannel
{
public:
////////////////////////////////////////////////////////////
explicit DataChannel(Ftp& owner);
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
DataChannel(const DataChannel&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
DataChannel& operator=(const DataChannel&) = delete;
////////////////////////////////////////////////////////////
Ftp::Response open(Ftp::TransferMode mode);
////////////////////////////////////////////////////////////
void send(std::istream& stream);
////////////////////////////////////////////////////////////
void receive(std::ostream& stream);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Ftp& m_ftp; //!< Reference to the owner Ftp instance
TcpSocket m_dataSocket; //!< Socket used for data transfers
};
////////////////////////////////////////////////////////////
Ftp::Response::Response(Status code, std::string message) : m_status(code), m_message(std::move(message))
{
}
////////////////////////////////////////////////////////////
bool Ftp::Response::isOk() const
{
return static_cast<int>(m_status) < 400;
}
////////////////////////////////////////////////////////////
Ftp::Response::Status Ftp::Response::getStatus() const
{
return m_status;
}
////////////////////////////////////////////////////////////
const std::string& Ftp::Response::getMessage() const
{
return m_message;
}
////////////////////////////////////////////////////////////
Ftp::DirectoryResponse::DirectoryResponse(const Ftp::Response& response) : Ftp::Response(response)
{
if (isOk())
{
// Extract the directory from the server response
const std::string::size_type begin = getMessage().find('"', 0);
const std::string::size_type end = getMessage().find('"', begin + 1);
m_directory = getMessage().substr(begin + 1, end - begin - 1);
}
}
////////////////////////////////////////////////////////////
const std::filesystem::path& Ftp::DirectoryResponse::getDirectory() const
{
return m_directory;
}
////////////////////////////////////////////////////////////
Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::string& data) : Ftp::Response(response)
{
if (isOk())
{
// Fill the array of strings
std::string::size_type lastPos = 0;
for (std::string::size_type pos = data.find("\r\n"); pos != std::string::npos; pos = data.find("\r\n", lastPos))
{
m_listing.push_back(data.substr(lastPos, pos - lastPos));
lastPos = pos + 2;
}
}
}
////////////////////////////////////////////////////////////
const std::vector<std::string>& Ftp::ListingResponse::getListing() const
{
return m_listing;
}
////////////////////////////////////////////////////////////
Ftp::~Ftp()
{
(void)disconnect();
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::connect(IpAddress server, unsigned short port, Time timeout)
{
// Connect to the server
if (m_commandSocket.connect(server, port, timeout) != Socket::Status::Done)
return Response(Response::Status::ConnectionFailed);
// Get the response to the connection
return getResponse();
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::login()
{
return login("anonymous", "user@sfml-dev.org");
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::login(const std::string& name, const std::string& password)
{
Response response = sendCommand("USER", name);
if (response.isOk())
response = sendCommand("PASS", password);
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::disconnect()
{
// Send the exit command
Response response = sendCommand("QUIT");
if (response.isOk())
m_commandSocket.disconnect();
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::keepAlive()
{
return sendCommand("NOOP");
}
////////////////////////////////////////////////////////////
Ftp::DirectoryResponse Ftp::getWorkingDirectory()
{
return {sendCommand("PWD")};
}
////////////////////////////////////////////////////////////
Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory)
{
// Open a data channel on default port (20) using ASCII transfer mode
std::ostringstream directoryData;
DataChannel data(*this);
Response response = data.open(TransferMode::Ascii);
if (response.isOk())
{
// Tell the server to send us the listing
response = sendCommand("NLST", directory);
if (response.isOk())
{
// Receive the listing
data.receive(directoryData);
// Get the response from the server
response = getResponse();
}
}
return {response, directoryData.str()};
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::changeDirectory(const std::string& directory)
{
return sendCommand("CWD", directory);
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::parentDirectory()
{
return sendCommand("CDUP");
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::createDirectory(const std::string& name)
{
return sendCommand("MKD", name);
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::deleteDirectory(const std::string& name)
{
return sendCommand("RMD", name);
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::renameFile(const std::filesystem::path& file, const std::filesystem::path& newName)
{
Response response = sendCommand("RNFR", file.string());
if (response.isOk())
response = sendCommand("RNTO", newName.string());
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::deleteFile(const std::filesystem::path& name)
{
return sendCommand("DELE", name.string());
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::download(const std::filesystem::path& remoteFile, const std::filesystem::path& localPath, TransferMode mode)
{
// Open a data channel using the given transfer mode
DataChannel data(*this);
Response response = data.open(mode);
if (response.isOk())
{
// Tell the server to start the transfer
response = sendCommand("RETR", remoteFile.string());
if (response.isOk())
{
// Create the file and truncate it if necessary
const std::filesystem::path filepath = localPath / remoteFile.filename();
std::ofstream file(filepath, std::ios_base::binary | std::ios_base::trunc);
if (!file)
return Response(Response::Status::InvalidFile);
// Receive the file data
data.receive(file);
// Close the file
file.close();
// Get the response from the server
response = getResponse();
// If the download was unsuccessful, delete the partial file
if (!response.isOk())
std::filesystem::remove(filepath);
}
}
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::upload(const std::filesystem::path& localFile,
const std::filesystem::path& remotePath,
TransferMode mode,
bool append)
{
// Get the contents of the file to send
std::ifstream file(localFile, std::ios_base::binary);
if (!file)
return Response(Response::Status::InvalidFile);
// Open a data channel using the given transfer mode
DataChannel data(*this);
Response response = data.open(mode);
if (response.isOk())
{
// Tell the server to start the transfer
response = sendCommand(append ? "APPE" : "STOR", (remotePath / localFile.filename()).string());
if (response.isOk())
{
// Send the file data
data.send(file);
// Get the response from the server
response = getResponse();
}
}
return response;
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::sendCommand(const std::string& command, const std::string& parameter)
{
// Build the command string
const std::string commandStr = parameter.empty() ? command + "\r\n" : command + " " + parameter + "\r\n";
// Send it to the server
if (m_commandSocket.send(commandStr.c_str(), commandStr.length()) != Socket::Status::Done)
return Response(Response::Status::ConnectionClosed);
// Get the response
return getResponse();
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::getResponse()
{
// We'll use a variable to keep track of the last valid code.
// It is useful in case of multi-lines responses, because the end of such a response
// will start by the same code
unsigned int lastCode = 0;
bool isInsideMultiline = false;
std::string message;
for (;;)
{
// Receive the response from the server
std::array<char, 1024> buffer{};
std::size_t length = 0;
if (m_receiveBuffer.empty())
{
if (m_commandSocket.receive(buffer.data(), buffer.size(), length) != Socket::Status::Done)
return Response(Response::Status::ConnectionClosed);
}
else
{
std::copy(m_receiveBuffer.begin(), m_receiveBuffer.end(), buffer.data());
length = m_receiveBuffer.size();
m_receiveBuffer.clear();
}
// There can be several lines inside the received buffer, extract them all
std::istringstream in(std::string(buffer.data(), length), std::ios_base::binary);
while (in)
{
// Try to extract the code
unsigned int code = 0;
if (in >> code)
{
// Extract the separator
char separator = 0;
in.get(separator);
// The '-' character means a multiline response
if ((separator == '-') && !isInsideMultiline)
{
// Set the multiline flag
isInsideMultiline = true;
// Keep track of the code
if (lastCode == 0)
lastCode = code;
// Extract the line
std::getline(in, message);
// Remove the ending '\r' (all lines are terminated by "\r\n")
message.erase(message.length() - 1);
message = separator + message + "\n";
}
else
{
// We must make sure that the code is the same, otherwise it means
// we haven't reached the end of the multiline response
if ((separator != '-') && ((code == lastCode) || (lastCode == 0)))
{
// Extract the line
std::string line;
std::getline(in, line);
// Remove the ending '\r' (all lines are terminated by "\r\n")
line.erase(line.length() - 1);
// Append it to the message
if (code == lastCode)
{
std::ostringstream out;
out << code << separator << line;
message += out.str();
}
else
{
message = separator + line;
}
// Save the remaining data for the next time getResponse() is called
m_receiveBuffer.assign(buffer.data() + static_cast<std::size_t>(in.tellg()),
length - static_cast<std::size_t>(in.tellg()));
// Return the response code and message
return Response(static_cast<Response::Status>(code), message);
}
// The line we just read was actually not a response,
// only a new part of the current multiline response
// Extract the line
std::string line;
std::getline(in, line);
if (!line.empty())
{
// Remove the ending '\r' (all lines are terminated by "\r\n")
line.erase(line.length() - 1);
// Append it to the current message
std::ostringstream out;
out << code << separator << line << '\n';
message += out.str();
}
}
}
else if (lastCode != 0)
{
// It seems we are in the middle of a multiline response
// Clear the error bits of the stream
in.clear();
// Extract the line
std::string line;
std::getline(in, line);
if (!line.empty())
{
// Remove the ending '\r' (all lines are terminated by "\r\n")
line.erase(line.length() - 1);
// Append it to the current message
message += line + "\n";
}
}
else
{
// Error: cannot extract the code, and we are not in a multiline response
return Response(Response::Status::InvalidResponse);
}
}
}
// We never reach there
}
////////////////////////////////////////////////////////////
Ftp::DataChannel::DataChannel(Ftp& owner) : m_ftp(owner)
{
}
////////////////////////////////////////////////////////////
Ftp::Response Ftp::DataChannel::open(Ftp::TransferMode mode)
{
// Open a data connection in active mode (we connect to the server)
Ftp::Response response = m_ftp.sendCommand("PASV");
if (response.isOk())
{
// Extract the connection address and port from the response
const std::string::size_type begin = response.getMessage().find_first_of("0123456789");
if (begin != std::string::npos)
{
std::array<std::uint8_t, 6> data{};
std::string str = response.getMessage().substr(begin);
std::size_t index = 0;
for (unsigned char& datum : data)
{
// Extract the current number
while (std::isdigit(str[index]))
{
datum = static_cast<std::uint8_t>(
static_cast<std::uint8_t>(datum * 10) + static_cast<std::uint8_t>(str[index] - '0'));
++index;
}
// Skip separator
++index;
}
// Reconstruct connection port and address
const auto port = static_cast<std::uint16_t>(data[4] * 256 + data[5]);
const IpAddress address(data[0], data[1], data[2], data[3]);
// Connect the data channel to the server
if (m_dataSocket.connect(address, port) == Socket::Status::Done)
{
// Translate the transfer mode to the corresponding FTP parameter
std::string modeStr;
switch (mode)
{
case Ftp::TransferMode::Binary:
modeStr = "I";
break;
case Ftp::TransferMode::Ascii:
modeStr = "A";
break;
case Ftp::TransferMode::Ebcdic:
modeStr = "E";
break;
}
// Set the transfer mode
response = m_ftp.sendCommand("TYPE", modeStr);
}
else
{
// Failed to connect to the server
response = Ftp::Response(Ftp::Response::Status::ConnectionFailed);
}
}
}
return response;
}
////////////////////////////////////////////////////////////
void Ftp::DataChannel::receive(std::ostream& stream)
{
// Receive data
std::array<char, 1024> buffer{};
std::size_t received = 0;
while (m_dataSocket.receive(buffer.data(), buffer.size(), received) == Socket::Status::Done)
{
stream.write(buffer.data(), static_cast<std::streamsize>(received));
if (!stream.good())
{
err() << "FTP Error: Writing to the file has failed" << std::endl;
break;
}
}
// Close the data socket
m_dataSocket.disconnect();
}
////////////////////////////////////////////////////////////
void Ftp::DataChannel::send(std::istream& stream)
{
// Send data
std::array<char, 1024> buffer{};
std::size_t count = 0;
for (;;)
{
// read some data from the stream
stream.read(buffer.data(), buffer.size());
if (!stream.good() && !stream.eof())
{
err() << "FTP Error: Reading from the file has failed" << std::endl;
break;
}
count = static_cast<std::size_t>(stream.gcount());
if (count > 0)
{
// we could read more data from the stream: send them
if (m_dataSocket.send(buffer.data(), count) != Socket::Status::Done)
break;
}
else
{
// no more data: exit the loop
break;
}
}
// Close the data socket
m_dataSocket.disconnect();
}
} // namespace sf

View File

@ -0,0 +1,399 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/Http.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Utils.hpp>
#include <algorithm>
#include <array>
#include <iterator>
#include <limits>
#include <ostream>
#include <sstream>
#include <utility>
#include <cctype>
#include <cstddef>
namespace sf
{
////////////////////////////////////////////////////////////
Http::Request::Request(const std::string& uri, Method method, const std::string& body) : m_method(method)
{
setUri(uri);
setBody(body);
}
////////////////////////////////////////////////////////////
void Http::Request::setField(const std::string& field, const std::string& value)
{
m_fields[toLower(field)] = value;
}
////////////////////////////////////////////////////////////
void Http::Request::setMethod(Http::Request::Method method)
{
m_method = method;
}
////////////////////////////////////////////////////////////
void Http::Request::setUri(const std::string& uri)
{
m_uri = uri;
// Make sure it starts with a '/'
if (m_uri.empty() || (m_uri[0] != '/'))
m_uri.insert(m_uri.begin(), '/');
}
////////////////////////////////////////////////////////////
void Http::Request::setHttpVersion(unsigned int major, unsigned int minor)
{
m_majorVersion = major;
m_minorVersion = minor;
}
////////////////////////////////////////////////////////////
void Http::Request::setBody(const std::string& body)
{
m_body = body;
}
////////////////////////////////////////////////////////////
std::string Http::Request::prepare() const
{
std::ostringstream out;
// Convert the method to its string representation
std::string method;
switch (m_method)
{
case Method::Get:
method = "GET";
break;
case Method::Post:
method = "POST";
break;
case Method::Head:
method = "HEAD";
break;
case Method::Put:
method = "PUT";
break;
case Method::Delete:
method = "DELETE";
break;
}
// Write the first line containing the request type
out << method << " " << m_uri << " ";
out << "HTTP/" << m_majorVersion << "." << m_minorVersion << "\r\n";
// Write fields
for (const auto& [fieldKey, fieldValue] : m_fields)
{
out << fieldKey << ": " << fieldValue << "\r\n";
}
// Use an extra \r\n to separate the header from the body
out << "\r\n";
// Add the body
out << m_body;
return out.str();
}
////////////////////////////////////////////////////////////
bool Http::Request::hasField(const std::string& field) const
{
return m_fields.find(toLower(field)) != m_fields.end();
}
////////////////////////////////////////////////////////////
const std::string& Http::Response::getField(const std::string& field) const
{
if (const auto it = m_fields.find(toLower(field)); it != m_fields.end())
{
return it->second;
}
static const std::string empty;
return empty;
}
////////////////////////////////////////////////////////////
Http::Response::Status Http::Response::getStatus() const
{
return m_status;
}
////////////////////////////////////////////////////////////
unsigned int Http::Response::getMajorHttpVersion() const
{
return m_majorVersion;
}
////////////////////////////////////////////////////////////
unsigned int Http::Response::getMinorHttpVersion() const
{
return m_minorVersion;
}
////////////////////////////////////////////////////////////
const std::string& Http::Response::getBody() const
{
return m_body;
}
////////////////////////////////////////////////////////////
void Http::Response::parse(const std::string& data)
{
std::istringstream in(data);
// Extract the HTTP version from the first line
std::string version;
if (in >> version)
{
if ((version.size() >= 8) && (version[6] == '.') && (toLower(version.substr(0, 5)) == "http/") &&
std::isdigit(version[5]) && std::isdigit(version[7]))
{
m_majorVersion = static_cast<unsigned int>(version[5] - '0');
m_minorVersion = static_cast<unsigned int>(version[7] - '0');
}
else
{
// Invalid HTTP version
m_status = Status::InvalidResponse;
return;
}
}
// Extract the status code from the first line
int status = 0;
if (in >> status)
{
m_status = static_cast<Status>(status);
}
else
{
// Invalid status code
m_status = Status::InvalidResponse;
return;
}
// Ignore the end of the first line
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Parse the other lines, which contain fields, one by one
parseFields(in);
m_body.clear();
// Determine whether the transfer is chunked
if (toLower(getField("transfer-encoding")) != "chunked")
{
// Not chunked - just read everything at once
std::copy(std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>(), std::back_inserter(m_body));
}
else
{
// Chunked - have to read chunk by chunk
std::size_t length = 0;
// Read all chunks, identified by a chunk-size not being 0
while (in >> std::hex >> length)
{
// Drop the rest of the line (chunk-extension)
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Copy the actual content data
std::istreambuf_iterator<char> it(in);
const std::istreambuf_iterator<char> itEnd;
for (std::size_t i = 0; ((i < length) && (it != itEnd)); ++i)
{
m_body.push_back(*it);
++it; // Iterate in separate expression to work around false positive -Wnull-dereference warning in GCC 12.1.0
}
}
// Drop the rest of the line (chunk-extension)
in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Read all trailers (if present)
parseFields(in);
}
}
////////////////////////////////////////////////////////////
void Http::Response::parseFields(std::istream& in)
{
std::string line;
while (std::getline(in, line) && (line.size() > 2))
{
const std::string::size_type pos = line.find(": ");
if (pos != std::string::npos)
{
// Extract the field name and its value
const std::string field = line.substr(0, pos);
std::string value = line.substr(pos + 2);
// Remove any trailing \r
if (!value.empty() && (*value.rbegin() == '\r'))
value.erase(value.size() - 1);
// Add the field
m_fields[toLower(field)] = value;
}
}
}
////////////////////////////////////////////////////////////
Http::Http(const std::string& host, unsigned short port)
{
setHost(host, port);
}
////////////////////////////////////////////////////////////
void Http::setHost(const std::string& host, unsigned short port)
{
// Check the protocol
if (toLower(host.substr(0, 7)) == "http://")
{
// HTTP protocol
m_hostName = host.substr(7);
m_port = (port != 0 ? port : 80);
}
else if (toLower(host.substr(0, 8)) == "https://")
{
// HTTPS protocol -- unsupported (requires encryption and certificates and stuff...)
err() << "HTTPS protocol is not supported by sf::Http" << std::endl;
m_hostName.clear();
m_port = 0;
}
else
{
// Undefined protocol - use HTTP
m_hostName = host;
m_port = (port != 0 ? port : 80);
}
// Remove any trailing '/' from the host name
if (!m_hostName.empty() && (*m_hostName.rbegin() == '/'))
m_hostName.erase(m_hostName.size() - 1);
m_host = IpAddress::resolve(m_hostName);
}
////////////////////////////////////////////////////////////
Http::Response Http::sendRequest(const Http::Request& request, Time timeout)
{
// First make sure that the request is valid -- add missing mandatory fields
Request toSend(request);
if (!toSend.hasField("From"))
{
toSend.setField("From", "user@sfml-dev.org");
}
if (!toSend.hasField("User-Agent"))
{
toSend.setField("User-Agent", "libsfml-network/3.x");
}
if (!toSend.hasField("Host"))
{
toSend.setField("Host", m_hostName);
}
if (!toSend.hasField("Content-Length"))
{
std::ostringstream out;
out << toSend.m_body.size();
toSend.setField("Content-Length", out.str());
}
if ((toSend.m_method == Request::Method::Post) && !toSend.hasField("Content-Type"))
{
toSend.setField("Content-Type", "application/x-www-form-urlencoded");
}
if ((toSend.m_majorVersion * 10 + toSend.m_minorVersion >= 11) && !toSend.hasField("Connection"))
{
toSend.setField("Connection", "close");
}
// Prepare the response
Response received;
// Connect the socket to the host
if (m_connection.connect(m_host.value(), m_port, timeout) == Socket::Status::Done)
{
// Convert the request to string and send it through the connected socket
const std::string requestStr = toSend.prepare();
if (!requestStr.empty())
{
// Send it through the socket
if (m_connection.send(requestStr.c_str(), requestStr.size()) == Socket::Status::Done)
{
// Wait for the server's response
std::string receivedStr;
std::size_t size = 0;
std::array<char, 1024> buffer{};
while (m_connection.receive(buffer.data(), buffer.size(), size) == Socket::Status::Done)
{
receivedStr.append(buffer.data(), buffer.data() + size);
}
// Build the Response object from the received data
received.parse(receivedStr);
}
}
// Close the connection
m_connection.disconnect();
}
return received;
}
} // namespace sf

View File

@ -0,0 +1,253 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/Http.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <istream>
#include <ostream>
#include <cstring>
namespace sf
{
////////////////////////////////////////////////////////////
const IpAddress IpAddress::Any(0, 0, 0, 0);
const IpAddress IpAddress::LocalHost(127, 0, 0, 1);
const IpAddress IpAddress::Broadcast(255, 255, 255, 255);
////////////////////////////////////////////////////////////
std::optional<IpAddress> IpAddress::resolve(std::string_view address)
{
using namespace std::string_view_literals;
if (address.empty())
{
// Not generating en error message here as resolution failure is a valid outcome.
return std::nullopt;
}
if (address == "255.255.255.255"sv)
{
// The broadcast address needs to be handled explicitly,
// because it is also the value returned by inet_addr on error
return Broadcast;
}
if (address == "0.0.0.0"sv)
return Any;
// Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx")
if (const std::uint32_t ip = inet_addr(address.data()); ip != INADDR_NONE)
return IpAddress(ntohl(ip));
// Not a valid address, try to convert it as a host name
addrinfo hints{}; // Zero-initialize
hints.ai_family = AF_INET;
addrinfo* result = nullptr;
if (getaddrinfo(address.data(), nullptr, &hints, &result) == 0 && result != nullptr)
{
sockaddr_in sin{};
std::memcpy(&sin, result->ai_addr, sizeof(*result->ai_addr));
const std::uint32_t ip = sin.sin_addr.s_addr;
freeaddrinfo(result);
return IpAddress(ntohl(ip));
}
// Not generating en error message here as resolution failure is a valid outcome.
return std::nullopt;
}
////////////////////////////////////////////////////////////
IpAddress::IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3) :
m_address(static_cast<std::uint32_t>((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3))
{
}
////////////////////////////////////////////////////////////
IpAddress::IpAddress(std::uint32_t address) : m_address(address)
{
}
////////////////////////////////////////////////////////////
std::string IpAddress::toString() const
{
in_addr address{};
address.s_addr = htonl(m_address);
return inet_ntoa(address);
}
////////////////////////////////////////////////////////////
std::uint32_t IpAddress::toInteger() const
{
return m_address;
}
////////////////////////////////////////////////////////////
std::optional<IpAddress> IpAddress::getLocalAddress()
{
// The method here is to connect a UDP socket to a public ip,
// and get the local socket address with the getsockname function.
// UDP connection will not send anything to the network, so this function won't cause any overhead.
// Create the socket
const SocketHandle sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == priv::SocketImpl::invalidSocket())
{
err() << "Failed to retrieve local address (invalid socket)" << std::endl;
return std::nullopt;
}
// Connect the socket to a public ip (here 1.1.1.1) on any
// port. This will give the local address of the network interface
// used for default routing which is usually what we want.
sockaddr_in address = priv::SocketImpl::createAddress(0x01010101, 9);
if (connect(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
{
priv::SocketImpl::close(sock);
err() << "Failed to retrieve local address (socket connection failure)" << std::endl;
return std::nullopt;
}
// Get the local address of the socket connection
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&address), &size) == -1)
{
priv::SocketImpl::close(sock);
err() << "Failed to retrieve local address (socket local address retrieval failure)" << std::endl;
return std::nullopt;
}
// Close the socket
priv::SocketImpl::close(sock);
// Finally build the IP address
return IpAddress(ntohl(address.sin_addr.s_addr));
}
////////////////////////////////////////////////////////////
std::optional<IpAddress> IpAddress::getPublicAddress(Time timeout)
{
// The trick here is more complicated, because the only way
// to get our public IP address is to get it from a distant computer.
// Here we get the web page from http://www.sfml-dev.org/ip-provider.php
// and parse the result to extract our IP address
// (not very hard: the web page contains only our IP address).
Http server("www.sfml-dev.org");
const Http::Request request("/ip-provider.php", Http::Request::Method::Get);
const Http::Response page = server.sendRequest(request, timeout);
const Http::Response::Status status = page.getStatus();
if (status == Http::Response::Status::Ok)
return IpAddress::resolve(page.getBody());
err() << "Failed to retrieve public address from external IP resolution server (HTTP response status "
<< static_cast<int>(status) << ")" << std::endl;
return std::nullopt;
}
////////////////////////////////////////////////////////////
bool operator==(IpAddress left, IpAddress right)
{
return !(left < right) && !(right < left);
}
////////////////////////////////////////////////////////////
bool operator!=(IpAddress left, IpAddress right)
{
return !(left == right);
}
////////////////////////////////////////////////////////////
bool operator<(IpAddress left, IpAddress right)
{
return left.m_address < right.m_address;
}
////////////////////////////////////////////////////////////
bool operator>(IpAddress left, IpAddress right)
{
return right < left;
}
////////////////////////////////////////////////////////////
bool operator<=(IpAddress left, IpAddress right)
{
return !(right < left);
}
////////////////////////////////////////////////////////////
bool operator>=(IpAddress left, IpAddress right)
{
return !(left < right);
}
////////////////////////////////////////////////////////////
std::istream& operator>>(std::istream& stream, std::optional<IpAddress>& address)
{
std::string str;
stream >> str;
address = IpAddress::resolve(str);
return stream;
}
////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& stream, IpAddress address)
{
return stream << address.toString();
}
} // namespace sf

View File

@ -0,0 +1,596 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/System/String.hpp>
#include <SFML/System/Utils.hpp>
#include <array>
#include <cassert>
#include <cstring>
#include <cwchar>
namespace sf
{
////////////////////////////////////////////////////////////
void Packet::append(const void* data, std::size_t sizeInBytes)
{
if (data && (sizeInBytes > 0))
{
const auto* begin = reinterpret_cast<const std::byte*>(data);
const auto* end = begin + sizeInBytes;
m_data.insert(m_data.end(), begin, end);
}
}
////////////////////////////////////////////////////////////
std::size_t Packet::getReadPosition() const
{
return m_readPos;
}
////////////////////////////////////////////////////////////
void Packet::clear()
{
m_data.clear();
m_readPos = 0;
m_isValid = true;
}
////////////////////////////////////////////////////////////
const void* Packet::getData() const
{
return !m_data.empty() ? m_data.data() : nullptr;
}
////////////////////////////////////////////////////////////
std::size_t Packet::getDataSize() const
{
return m_data.size();
}
////////////////////////////////////////////////////////////
bool Packet::endOfPacket() const
{
return m_readPos >= m_data.size();
}
////////////////////////////////////////////////////////////
Packet::operator bool() const
{
return m_isValid;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(bool& data)
{
std::uint8_t value = 0;
if (*this >> value)
data = (value != 0);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int8_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint8_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int16_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = static_cast<std::int16_t>(ntohs(static_cast<std::uint16_t>(data)));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint16_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = ntohs(data);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int32_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = static_cast<std::int32_t>(ntohl(static_cast<std::uint32_t>(data)));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint32_t& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = ntohl(data);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::int64_t& data)
{
if (checkSize(sizeof(data)))
{
// Since ntohll is not available everywhere, we have to convert
// to network byte order (big endian) manually
std::array<std::byte, sizeof(data)> bytes{};
std::memcpy(bytes.data(), &m_data[m_readPos], bytes.size());
data = toInteger<std::int64_t>(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::uint64_t& data)
{
if (checkSize(sizeof(data)))
{
// Since ntohll is not available everywhere, we have to convert
// to network byte order (big endian) manually
std::array<std::byte, sizeof(data)> bytes{};
std::memcpy(bytes.data(), &m_data[m_readPos], sizeof(data));
data = toInteger<std::uint64_t>(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(float& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(double& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
m_readPos += sizeof(data);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(char* data)
{
assert(data && "Packet::operator>> Data must not be null");
// First extract string length
std::uint32_t length = 0;
*this >> length;
if ((length > 0) && checkSize(length))
{
// Then extract characters
std::memcpy(data, &m_data[m_readPos], length);
data[length] = '\0';
// Update reading position
m_readPos += length;
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::string& data)
{
// First extract string length
std::uint32_t length = 0;
*this >> length;
data.clear();
if ((length > 0) && checkSize(length))
{
// Then extract characters
data.assign(reinterpret_cast<char*>(&m_data[m_readPos]), length);
// Update reading position
m_readPos += length;
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(wchar_t* data)
{
assert(data && "Packet::operator>> Data must not be null");
// First extract string length
std::uint32_t length = 0;
*this >> length;
if ((length > 0) && checkSize(length * sizeof(std::uint32_t)))
{
// Then extract characters
for (std::uint32_t i = 0; i < length; ++i)
{
std::uint32_t character = 0;
*this >> character;
data[i] = static_cast<wchar_t>(character);
}
data[length] = L'\0';
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(std::wstring& data)
{
// First extract string length
std::uint32_t length = 0;
*this >> length;
data.clear();
if ((length > 0) && checkSize(length * sizeof(std::uint32_t)))
{
// Then extract characters
for (std::uint32_t i = 0; i < length; ++i)
{
std::uint32_t character = 0;
*this >> character;
data += static_cast<wchar_t>(character);
}
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator>>(String& data)
{
// First extract the string length
std::uint32_t length = 0;
*this >> length;
data.clear();
if ((length > 0) && checkSize(length * sizeof(std::uint32_t)))
{
// Then extract characters
for (std::uint32_t i = 0; i < length; ++i)
{
std::uint32_t character = 0;
*this >> character;
data += static_cast<char32_t>(character);
}
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(bool data)
{
*this << static_cast<std::uint8_t>(data);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int8_t data)
{
append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint8_t data)
{
append(&data, sizeof(data));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int16_t data)
{
const auto toWrite = static_cast<std::int16_t>(htons(static_cast<std::uint16_t>(data)));
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint16_t data)
{
const std::uint16_t toWrite = htons(data);
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int32_t data)
{
const auto toWrite = static_cast<std::int32_t>(htonl(static_cast<std::uint32_t>(data)));
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint32_t data)
{
const std::uint32_t toWrite = htonl(data);
append(&toWrite, sizeof(toWrite));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::int64_t data)
{
// Since htonll is not available everywhere, we have to convert
// to network byte order (big endian) manually
const std::array toWrite = {static_cast<std::uint8_t>((data >> 56) & 0xFF),
static_cast<std::uint8_t>((data >> 48) & 0xFF),
static_cast<std::uint8_t>((data >> 40) & 0xFF),
static_cast<std::uint8_t>((data >> 32) & 0xFF),
static_cast<std::uint8_t>((data >> 24) & 0xFF),
static_cast<std::uint8_t>((data >> 16) & 0xFF),
static_cast<std::uint8_t>((data >> 8) & 0xFF),
static_cast<std::uint8_t>((data) & 0xFF)};
append(toWrite.data(), toWrite.size());
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(std::uint64_t data)
{
// Since htonll is not available everywhere, we have to convert
// to network byte order (big endian) manually
const std::array toWrite = {static_cast<std::uint8_t>((data >> 56) & 0xFF),
static_cast<std::uint8_t>((data >> 48) & 0xFF),
static_cast<std::uint8_t>((data >> 40) & 0xFF),
static_cast<std::uint8_t>((data >> 32) & 0xFF),
static_cast<std::uint8_t>((data >> 24) & 0xFF),
static_cast<std::uint8_t>((data >> 16) & 0xFF),
static_cast<std::uint8_t>((data >> 8) & 0xFF),
static_cast<std::uint8_t>((data) & 0xFF)};
append(toWrite.data(), toWrite.size());
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)
{
assert(data && "Packet::operator<< Data must not be null");
// First insert string length
const auto length = static_cast<std::uint32_t>(std::strlen(data));
*this << length;
// Then insert characters
append(data, length * sizeof(char));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const std::string& data)
{
// First insert string length
const auto length = static_cast<std::uint32_t>(data.size());
*this << length;
// Then insert characters
if (length > 0)
append(data.c_str(), length * sizeof(std::string::value_type));
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const wchar_t* data)
{
assert(data && "Packet::operator<< Data must not be null");
// First insert string length
const auto length = static_cast<std::uint32_t>(std::wcslen(data));
*this << length;
// Then insert characters
for (const wchar_t* c = data; *c != L'\0'; ++c)
*this << static_cast<std::uint32_t>(*c);
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const std::wstring& data)
{
// First insert string length
const auto length = static_cast<std::uint32_t>(data.size());
*this << length;
// Then insert characters
if (length > 0)
{
for (const wchar_t c : data)
*this << static_cast<std::uint32_t>(c);
}
return *this;
}
////////////////////////////////////////////////////////////
Packet& Packet::operator<<(const String& data)
{
// First insert the string length
const auto length = static_cast<std::uint32_t>(data.getSize());
*this << length;
// Then insert characters
if (length > 0)
{
for (const unsigned int datum : data)
*this << datum;
}
return *this;
}
////////////////////////////////////////////////////////////
bool Packet::checkSize(std::size_t size)
{
// Determine if size is big enough to trigger an overflow
const bool overflowDetected = m_readPos + size < m_readPos;
m_isValid = m_isValid && (m_readPos + size <= m_data.size()) && !overflowDetected;
return m_isValid;
}
////////////////////////////////////////////////////////////
const void* Packet::onSend(std::size_t& size)
{
size = getDataSize();
return getData();
}
////////////////////////////////////////////////////////////
void Packet::onReceive(const void* data, std::size_t size)
{
append(data, size);
}
} // namespace sf

View File

@ -0,0 +1,175 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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>
#include <ostream>
#include <utility>
namespace sf
{
////////////////////////////////////////////////////////////
Socket::Socket(Type type) : m_type(type), m_socket(priv::SocketImpl::invalidSocket())
{
}
////////////////////////////////////////////////////////////
Socket::~Socket()
{
// Close the socket before it gets destructed
close();
}
////////////////////////////////////////////////////////////
Socket::Socket(Socket&& socket) noexcept :
m_type(socket.m_type),
m_socket(std::exchange(socket.m_socket, priv::SocketImpl::invalidSocket())),
m_isBlocking(socket.m_isBlocking)
{
}
////////////////////////////////////////////////////////////
Socket& Socket::operator=(Socket&& socket) noexcept
{
if (&socket == this)
return *this;
close();
m_type = socket.m_type;
m_socket = std::exchange(socket.m_socket, priv::SocketImpl::invalidSocket());
m_isBlocking = socket.m_isBlocking;
return *this;
}
////////////////////////////////////////////////////////////
void Socket::setBlocking(bool blocking)
{
// Apply if the socket is already created
if (m_socket != priv::SocketImpl::invalidSocket())
priv::SocketImpl::setBlocking(m_socket, blocking);
m_isBlocking = blocking;
}
////////////////////////////////////////////////////////////
bool Socket::isBlocking() const
{
return m_isBlocking;
}
////////////////////////////////////////////////////////////
SocketHandle Socket::getNativeHandle() const
{
return m_socket;
}
////////////////////////////////////////////////////////////
void Socket::create()
{
// Don't create the socket if it already exists
if (m_socket == priv::SocketImpl::invalidSocket())
{
const SocketHandle handle = socket(PF_INET, m_type == Type::Tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
if (handle == priv::SocketImpl::invalidSocket())
{
err() << "Failed to create socket" << std::endl;
return;
}
create(handle);
}
}
////////////////////////////////////////////////////////////
void Socket::create(SocketHandle handle)
{
// Don't create the socket if it already exists
if (m_socket == priv::SocketImpl::invalidSocket())
{
// Assign the new handle
m_socket = handle;
// Set the current blocking state
setBlocking(m_isBlocking);
if (m_type == Type::Tcp)
{
// Disable the Nagle algorithm (i.e. removes buffering of TCP packets)
int yes = 1;
if (setsockopt(m_socket, 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;
}
// On macOS, disable the SIGPIPE signal on disconnection
#ifdef SFML_SYSTEM_MACOS
if (setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&yes), sizeof(yes)) == -1)
{
err() << "Failed to set socket option \"SO_NOSIGPIPE\"" << std::endl;
}
#endif
}
else
{
// Enable broadcast by default for UDP sockets
int yes = 1;
if (setsockopt(m_socket, 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 (m_socket != priv::SocketImpl::invalidSocket())
{
priv::SocketImpl::close(m_socket);
m_socket = priv::SocketImpl::invalidSocket();
}
}
} // namespace sf

View File

@ -0,0 +1,123 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketHandle.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <SFML/System/Win32/WindowsHeader.hpp>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstddef>
#endif
#include <cstdint>
namespace sf::priv
{
////////////////////////////////////////////////////////////
/// \brief Helper class implementing all the non-portable
/// socket stuff
///
////////////////////////////////////////////////////////////
class SocketImpl
{
public:
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_WINDOWS)
using AddrLength = int;
using Size = int;
#else
using AddrLength = socklen_t;
using Size = std::size_t;
#endif
////////////////////////////////////////////////////////////
/// \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(std::uint32_t 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 sf::priv

View File

@ -0,0 +1,209 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/Network/SocketSelector.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <memory>
#include <ostream>
#include <utility>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace sf
{
////////////////////////////////////////////////////////////
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
int socketCount{}; //!< Number of socket handles
};
////////////////////////////////////////////////////////////
SocketSelector::SocketSelector() : m_impl(std::make_unique<SocketSelectorImpl>())
{
clear();
}
////////////////////////////////////////////////////////////
SocketSelector::~SocketSelector() = default;
////////////////////////////////////////////////////////////
SocketSelector::SocketSelector(const SocketSelector& copy) : m_impl(std::make_unique<SocketSelectorImpl>(*copy.m_impl))
{
}
////////////////////////////////////////////////////////////
SocketSelector& SocketSelector::operator=(const SocketSelector& right)
{
SocketSelector temp(right);
std::swap(m_impl, temp.m_impl);
return *this;
}
////////////////////////////////////////////////////////////
SocketSelector::SocketSelector(SocketSelector&&) noexcept = default;
////////////////////////////////////////////////////////////]
SocketSelector& SocketSelector::operator=(SocketSelector&&) noexcept = default;
////////////////////////////////////////////////////////////
void SocketSelector::add(Socket& socket)
{
const SocketHandle handle = socket.getNativeHandle();
if (handle != priv::SocketImpl::invalidSocket())
{
#if defined(SFML_SYSTEM_WINDOWS)
if (m_impl->socketCount >= FD_SETSIZE)
{
err() << "The socket can't be added to the selector because the "
<< "selector is full. This is a limitation of your operating "
<< "system's FD_SETSIZE setting.";
return;
}
if (FD_ISSET(handle, &m_impl->allSockets))
return;
++m_impl->socketCount;
#else
if (handle >= FD_SETSIZE)
{
err() << "The socket can't be added to the selector because its "
<< "ID is too high. This is a limitation of your operating "
<< "system's FD_SETSIZE setting.";
return;
}
// SocketHandle is an int in POSIX
m_impl->maxSocket = std::max(m_impl->maxSocket, handle);
#endif
FD_SET(handle, &m_impl->allSockets);
}
}
////////////////////////////////////////////////////////////
void SocketSelector::remove(Socket& socket)
{
const SocketHandle handle = socket.getNativeHandle();
if (handle != priv::SocketImpl::invalidSocket())
{
#if defined(SFML_SYSTEM_WINDOWS)
if (!FD_ISSET(handle, &m_impl->allSockets))
return;
--m_impl->socketCount;
#else
if (handle >= FD_SETSIZE)
return;
#endif
FD_CLR(handle, &m_impl->allSockets);
FD_CLR(handle, &m_impl->socketsReady);
}
}
////////////////////////////////////////////////////////////
void SocketSelector::clear()
{
FD_ZERO(&m_impl->allSockets);
FD_ZERO(&m_impl->socketsReady);
m_impl->maxSocket = 0;
m_impl->socketCount = 0;
}
////////////////////////////////////////////////////////////
bool SocketSelector::wait(Time timeout)
{
// Setup the timeout
timeval time{};
time.tv_sec = static_cast<long>(timeout.asMicroseconds() / 1000000);
time.tv_usec = static_cast<int>(timeout.asMicroseconds() % 1000000);
// Initialize the set that will contain the sockets that are ready
m_impl->socketsReady = m_impl->allSockets;
// Wait until one of the sockets is ready for reading, or timeout is reached
// The first parameter is ignored on Windows
const int count = select(m_impl->maxSocket + 1, &m_impl->socketsReady, nullptr, nullptr, timeout != Time::Zero ? &time : nullptr);
return count > 0;
}
////////////////////////////////////////////////////////////
bool SocketSelector::isReady(Socket& socket) const
{
const SocketHandle handle = socket.getNativeHandle();
if (handle != priv::SocketImpl::invalidSocket())
{
#if !defined(SFML_SYSTEM_WINDOWS)
if (handle >= FD_SETSIZE)
return false;
#endif
return FD_ISSET(handle, &m_impl->socketsReady) != 0;
}
return false;
}
} // namespace sf

View File

@ -0,0 +1,132 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/SocketImpl.hpp>
#include <SFML/Network/TcpListener.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Err.hpp>
#include <ostream>
namespace sf
{
////////////////////////////////////////////////////////////
TcpListener::TcpListener() : Socket(Type::Tcp)
{
}
////////////////////////////////////////////////////////////
unsigned short TcpListener::getLocalPort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the local end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status TcpListener::listen(unsigned short port, IpAddress address)
{
// Close the socket if it is already bound
close();
// Create the internal socket if it doesn't exist
create();
// Check if the address is valid
if (address == IpAddress::Broadcast)
return Status::Error;
// Bind the socket to the specified port
sockaddr_in addr = priv::SocketImpl::createAddress(address.toInteger(), port);
if (bind(getNativeHandle(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
{
// Not likely to happen, but...
err() << "Failed to bind listener socket to port " << port << std::endl;
return Status::Error;
}
// Listen to the bound port
if (::listen(getNativeHandle(), SOMAXCONN) == -1)
{
// Oops, socket is deaf
err() << "Failed to listen to port " << port << std::endl;
return Status::Error;
}
return Status::Done;
}
////////////////////////////////////////////////////////////
void TcpListener::close()
{
// Simply close the socket
Socket::close();
}
////////////////////////////////////////////////////////////
Socket::Status TcpListener::accept(TcpSocket& socket)
{
// Make sure that we're listening
if (getNativeHandle() == priv::SocketImpl::invalidSocket())
{
err() << "Failed to accept a new connection, the socket is not listening" << std::endl;
return Status::Error;
}
// Accept a new connection
sockaddr_in address{};
priv::SocketImpl::AddrLength length = sizeof(address);
const SocketHandle remote = ::accept(getNativeHandle(), 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 Status::Done;
}
} // namespace sf

View File

@ -0,0 +1,429 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Err.hpp>
#include <algorithm>
#include <array>
#include <ostream>
#include <cstring>
#ifdef _MSC_VER
#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro
#endif
namespace
{
// Low-level send/receive flags (OS-dependent)
#ifdef SFML_SYSTEM_LINUX
constexpr int flags = MSG_NOSIGNAL;
#else
constexpr int flags = 0;
#endif
} // namespace
namespace sf
{
////////////////////////////////////////////////////////////
TcpSocket::TcpSocket() : Socket(Type::Tcp)
{
}
////////////////////////////////////////////////////////////
unsigned short TcpSocket::getLocalPort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the local end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
std::optional<IpAddress> TcpSocket::getRemoteAddress() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the remote end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getpeername(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return IpAddress(ntohl(address.sin_addr.s_addr));
}
}
// We failed to retrieve the address
return std::nullopt;
}
////////////////////////////////////////////////////////////
unsigned short TcpSocket::getRemotePort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the remote end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getpeername(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), &size) != -1)
{
return ntohs(address.sin_port);
}
}
// We failed to retrieve the port
return 0;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout)
{
// Disconnect the socket if it is already connected
disconnect();
// 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 <= Time::Zero)
{
// ----- We're not using a timeout: just try to connect -----
// Connect the socket
if (::connect(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
return priv::SocketImpl::getErrorStatus();
// Connection succeeded
return Status::Done;
}
// ----- We're using a timeout: we'll need a few tricks to make it work -----
// Save the previous blocking state
const 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(getNativeHandle(), reinterpret_cast<sockaddr*>(&address), sizeof(address)) >= 0)
{
// We got instantly connected! (it may no happen a lot...)
setBlocking(blocking);
return Status::Done;
}
// Get the error status
Status status = priv::SocketImpl::getErrorStatus();
// If we were in non-blocking mode, return immediately
if (!blocking)
return status;
// Otherwise, wait until something happens to our socket (success, timeout or error)
if (status == Socket::Status::NotReady)
{
// Setup the selector
fd_set selector;
FD_ZERO(&selector);
FD_SET(getNativeHandle(), &selector);
// Setup the timeout
timeval time{};
time.tv_sec = static_cast<long>(timeout.asMicroseconds() / 1000000);
time.tv_usec = static_cast<int>(timeout.asMicroseconds() % 1000000);
// Wait for something to write on our socket (which means that the connection request has returned)
if (select(static_cast<int>(getNativeHandle() + 1), nullptr, &selector, nullptr, &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().has_value())
{
// Connection accepted
status = 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()
{
// Close the socket
close();
// Reset the pending packet data
m_pendingPacket = PendingPacket();
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(const void* data, std::size_t size)
{
if (!isBlocking())
err() << "Warning: Partial sends might not be handled properly." << std::endl;
std::size_t sent = 0;
return send(data, size, sent);
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(const void* data, std::size_t size, std::size_t& sent)
{
// Check the parameters
if (!data || (size == 0))
{
err() << "Cannot send data over the network (no data to send)" << std::endl;
return Status::Error;
}
// Loop until every byte has been sent
int result = 0;
for (sent = 0; sent < size; sent += static_cast<std::size_t>(result))
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Send a chunk of data
result = static_cast<int>(::send(getNativeHandle(),
static_cast<const char*>(data) + sent,
static_cast<priv::SocketImpl::Size>(size - sent),
flags));
#pragma GCC diagnostic pop
// Check for errors
if (result < 0)
{
const Status status = priv::SocketImpl::getErrorStatus();
if ((status == Status::NotReady) && sent)
return Status::Partial;
return status;
}
}
return Status::Done;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::receive(void* data, std::size_t size, std::size_t& received)
{
// First clear the variables to fill
received = 0;
// Check the destination buffer
if (!data)
{
err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl;
return Status::Error;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Receive a chunk of bytes
const int sizeReceived = static_cast<int>(
recv(getNativeHandle(), static_cast<char*>(data), static_cast<priv::SocketImpl::Size>(size), flags));
#pragma GCC diagnostic pop
// Check the number of bytes received
if (sizeReceived > 0)
{
received = static_cast<std::size_t>(sizeReceived);
return Status::Done;
}
if (sizeReceived == 0)
{
return Socket::Status::Disconnected;
}
return priv::SocketImpl::getErrorStatus();
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::send(Packet& packet)
{
// TCP is a stream protocol, it doesn't preserve messages boundaries.
// This means that we have to send the packet size first, so that the
// receiver knows the actual end of the packet in the data stream.
// We allocate an extra memory block so that the size can be sent
// together with the data in a single call. This may seem inefficient,
// but it is actually required to avoid partial send, which could cause
// data corruption on the receiving end.
// Get the data to send from the packet
std::size_t size = 0;
const void* data = packet.onSend(size);
// First convert the packet size to network byte order
std::uint32_t packetSize = htonl(static_cast<std::uint32_t>(size));
// Allocate memory for the data block to send
m_blockToSendBuffer.resize(sizeof(packetSize) + size);
// Copy the packet size and data into the block to send
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnull-dereference" // False positive.
std::memcpy(m_blockToSendBuffer.data(), &packetSize, sizeof(packetSize));
#pragma GCC diagnostic pop
if (size > 0)
std::memcpy(m_blockToSendBuffer.data() + sizeof(packetSize), data, size);
// These warnings are ignored here for portability, as even on Windows the
// signature of `send` might change depending on whether Win32 or MinGW is
// being used.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
// Send the data block
std::size_t sent = 0;
const Status status = send(m_blockToSendBuffer.data() + packet.m_sendPos,
static_cast<priv::SocketImpl::Size>(m_blockToSendBuffer.size() - packet.m_sendPos),
sent);
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
// In the case of a partial send, record the location to resume from
if (status == Status::Partial)
{
packet.m_sendPos += sent;
}
else if (status == Status::Done)
{
packet.m_sendPos = 0;
}
return status;
}
////////////////////////////////////////////////////////////
Socket::Status TcpSocket::receive(Packet& packet)
{
// First clear the variables to fill
packet.clear();
// We start by getting the size of the incoming packet
std::uint32_t packetSize = 0;
std::size_t received = 0;
if (m_pendingPacket.sizeReceived < sizeof(m_pendingPacket.size))
{
// Loop until we've received the entire size of the packet
// (even a 4 byte variable may be received in more than one call)
while (m_pendingPacket.sizeReceived < sizeof(m_pendingPacket.size))
{
char* data = reinterpret_cast<char*>(&m_pendingPacket.size) + m_pendingPacket.sizeReceived;
const Status status = receive(data, sizeof(m_pendingPacket.size) - m_pendingPacket.sizeReceived, received);
m_pendingPacket.sizeReceived += received;
if (status != Status::Done)
return status;
}
// The packet size has been fully received
packetSize = ntohl(m_pendingPacket.size);
}
else
{
// The packet size has already been received in a previous call
packetSize = ntohl(m_pendingPacket.size);
}
// Loop until we receive all the packet data
std::array<char, 1024> buffer{};
while (m_pendingPacket.data.size() < packetSize)
{
// Receive a chunk of data
const std::size_t sizeToGet = std::min(packetSize - m_pendingPacket.data.size(), buffer.size());
const Status status = receive(buffer.data(), sizeToGet, received);
if (status != Status::Done)
return status;
// Append it into the packet
if (received > 0)
{
m_pendingPacket.data.resize(m_pendingPacket.data.size() + received);
std::byte* begin = m_pendingPacket.data.data() + m_pendingPacket.data.size() - received;
std::memcpy(begin, buffer.data(), received);
}
}
// We have received all the packet data: we can copy it to the user packet
if (!m_pendingPacket.data.empty())
packet.onReceive(m_pendingPacket.data.data(), m_pendingPacket.data.size());
// Clear the pending packet data
m_pendingPacket = PendingPacket();
return Status::Done;
}
} // namespace sf

View File

@ -0,0 +1,223 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/SocketImpl.hpp>
#include <SFML/Network/UdpSocket.hpp>
#include <SFML/System/Err.hpp>
#include <ostream>
#include <cstddef>
namespace sf
{
////////////////////////////////////////////////////////////
UdpSocket::UdpSocket() : Socket(Type::Udp)
{
}
////////////////////////////////////////////////////////////
unsigned short UdpSocket::getLocalPort() const
{
if (getNativeHandle() != priv::SocketImpl::invalidSocket())
{
// Retrieve information about the local end of the socket
sockaddr_in address{};
priv::SocketImpl::AddrLength size = sizeof(address);
if (getsockname(getNativeHandle(), 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, IpAddress address)
{
// Close the socket if it is already bound
close();
// Create the internal socket if it doesn't exist
create();
// Check if the address is valid
if (address == IpAddress::Broadcast)
return Status::Error;
// Bind the socket
sockaddr_in addr = priv::SocketImpl::createAddress(address.toInteger(), port);
if (::bind(getNativeHandle(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
{
err() << "Failed to bind socket to port " << port << std::endl;
return Status::Error;
}
return Status::Done;
}
////////////////////////////////////////////////////////////
void UdpSocket::unbind()
{
// Simply close the socket
close();
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort)
{
// Create the internal socket if it doesn't exist
create();
// Make sure that all the data will fit in one datagram
if (size > MaxDatagramSize)
{
err() << "Cannot send data over the network "
<< "(the number of bytes to send is greater than sf::UdpSocket::MaxDatagramSize)" << std::endl;
return Status::Error;
}
// Build the target address
sockaddr_in address = priv::SocketImpl::createAddress(remoteAddress.toInteger(), remotePort);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Send the data (unlike TCP, all the data is always sent in one call)
const int sent = static_cast<int>(
sendto(getNativeHandle(),
static_cast<const char*>(data),
static_cast<priv::SocketImpl::Size>(size),
0,
reinterpret_cast<sockaddr*>(&address),
sizeof(address)));
#pragma GCC diagnostic pop
// Check for errors
if (sent < 0)
return priv::SocketImpl::getErrorStatus();
return Status::Done;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::receive(void* data,
std::size_t size,
std::size_t& received,
std::optional<IpAddress>& remoteAddress,
unsigned short& remotePort)
{
// First clear the variables to fill
received = 0;
remoteAddress = std::nullopt;
remotePort = 0;
// Check the destination buffer
if (!data)
{
err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl;
return Status::Error;
}
// Data that will be filled with the other computer's address
sockaddr_in address = priv::SocketImpl::createAddress(INADDR_ANY, 0);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuseless-cast"
// Receive a chunk of bytes
priv::SocketImpl::AddrLength addressSize = sizeof(address);
const int sizeReceived = static_cast<int>(
recvfrom(getNativeHandle(),
static_cast<char*>(data),
static_cast<priv::SocketImpl::Size>(size),
0,
reinterpret_cast<sockaddr*>(&address),
&addressSize));
#pragma GCC diagnostic pop
// Check for errors
if (sizeReceived < 0)
return priv::SocketImpl::getErrorStatus();
// Fill the sender information
received = static_cast<std::size_t>(sizeReceived);
remoteAddress = IpAddress(ntohl(address.sin_addr.s_addr));
remotePort = ntohs(address.sin_port);
return Status::Done;
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort)
{
// UDP is a datagram-oriented protocol (as opposed to TCP which is a stream protocol).
// Sending one datagram is almost safe: it may be lost but if it's received, then its data
// is guaranteed to be ok. However, splitting a packet into multiple datagrams would be highly
// unreliable, since datagrams may be reordered, dropped or mixed between different sources.
// That's why SFML imposes a limit on packet size so that they can be sent in a single datagram.
// This also removes the overhead associated to packets -- there's no size to send in addition
// to the packet's data.
// Get the data to send from the packet
std::size_t size = 0;
const void* data = packet.onSend(size);
// Send it
return send(data, size, remoteAddress, remotePort);
}
////////////////////////////////////////////////////////////
Socket::Status UdpSocket::receive(Packet& packet, std::optional<IpAddress>& remoteAddress, unsigned short& remotePort)
{
// See the detailed comment in send(Packet) above.
// Receive the datagram
std::size_t received = 0;
const Status status = receive(m_buffer.data(), m_buffer.size(), received, remoteAddress, remotePort);
// If we received valid data, we can copy it to the user packet
packet.clear();
if ((status == Status::Done) && (received > 0))
packet.onReceive(m_buffer.data(), received);
return status;
}
} // namespace sf

View File

@ -0,0 +1,111 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/SocketImpl.hpp>
#include <SFML/System/Err.hpp>
#include <fcntl.h>
#include <ostream>
#include <cerrno>
namespace sf::priv
{
////////////////////////////////////////////////////////////
sockaddr_in SocketImpl::createAddress(std::uint32_t address, unsigned short port)
{
auto addr = sockaddr_in();
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
#if defined(SFML_SYSTEM_MACOS)
addr.sin_len = sizeof(addr);
#endif
return addr;
}
////////////////////////////////////////////////////////////
SocketHandle SocketImpl::invalidSocket()
{
return -1;
}
////////////////////////////////////////////////////////////
void SocketImpl::close(SocketHandle sock)
{
::close(sock);
}
////////////////////////////////////////////////////////////
void SocketImpl::setBlocking(SocketHandle sock, bool block)
{
const int status = fcntl(sock, F_GETFL);
if (block)
{
if (fcntl(sock, F_SETFL, status & ~O_NONBLOCK) == -1)
err() << "Failed to set file status flags: " << errno << std::endl;
}
else
{
if (fcntl(sock, F_SETFL, status | O_NONBLOCK) == -1)
err() << "Failed to set file status flags: " << errno << std::endl;
}
}
////////////////////////////////////////////////////////////
Socket::Status SocketImpl::getErrorStatus()
{
// The following are sometimes equal to EWOULDBLOCK,
// so we have to make a special case for them in order
// to avoid having double values in the switch case
if ((errno == EAGAIN) || (errno == EINPROGRESS))
return Socket::Status::NotReady;
// clang-format off
switch (errno)
{
case EWOULDBLOCK: return Socket::Status::NotReady;
case ECONNABORTED: return Socket::Status::Disconnected;
case ECONNRESET: return Socket::Status::Disconnected;
case ETIMEDOUT: return Socket::Status::Disconnected;
case ENETRESET: return Socket::Status::Disconnected;
case ENOTCONN: return Socket::Status::Disconnected;
case EPIPE: return Socket::Status::Disconnected;
default: return Socket::Status::Error;
}
// clang-format on
}
} // namespace sf::priv

View File

@ -0,0 +1,110 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// 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/SocketImpl.hpp>
#include <cstdint>
namespace
{
////////////////////////////////////////////////////////////
// Windows needs some initialization and cleanup to get
// sockets working properly... so let's create a class that will
// do it automatically
////////////////////////////////////////////////////////////
struct SocketInitializer
{
SocketInitializer()
{
WSADATA init;
WSAStartup(MAKEWORD(2, 2), &init);
}
~SocketInitializer()
{
WSACleanup();
}
} globalInitializer;
} // namespace
namespace sf::priv
{
////////////////////////////////////////////////////////////
sockaddr_in SocketImpl::createAddress(std::uint32_t address, unsigned short port)
{
auto addr = sockaddr_in();
addr.sin_addr.s_addr = htonl(address);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
return addr;
}
////////////////////////////////////////////////////////////
SocketHandle SocketImpl::invalidSocket()
{
return INVALID_SOCKET;
}
////////////////////////////////////////////////////////////
void SocketImpl::close(SocketHandle sock)
{
closesocket(sock);
}
////////////////////////////////////////////////////////////
void SocketImpl::setBlocking(SocketHandle sock, bool block)
{
u_long blocking = block ? 0 : 1;
ioctlsocket(sock, static_cast<long>(FIONBIO), &blocking);
}
////////////////////////////////////////////////////////////
Socket::Status SocketImpl::getErrorStatus()
{
// clang-format off
switch (WSAGetLastError())
{
case WSAEWOULDBLOCK: return Socket::Status::NotReady;
case WSAEALREADY: return Socket::Status::NotReady;
case WSAECONNABORTED: return Socket::Status::Disconnected;
case WSAECONNRESET: return Socket::Status::Disconnected;
case WSAETIMEDOUT: return Socket::Status::Disconnected;
case WSAENETRESET: return Socket::Status::Disconnected;
case WSAENOTCONN: return Socket::Status::Disconnected;
case WSAEISCONN: return Socket::Status::Done; // when connecting a non-blocking socket
default: return Socket::Status::Error;
}
// clang-format on
}
} // namespace sf::priv

View File

@ -1,70 +1,80 @@
project"vorbis" local m = {}
cppdialect"c++17"
kind"staticLib"
targetdir (libout)
staticruntime "off"
objdir(intdir)
links{"ogg"} local scriptdir = path.getabsolute(path.getdirectory(_SCRIPT))
local ogg = require("vendor/ogg/build-ogg")
function m.generateproject(liboutdir, intdir)
project"vorbis"
language"C" -- c++ will mangle names and sfml wont build
kind"staticLib"
targetdir (liboutdir)
objdir(intdir)
warnings"Off"
ogg.link()
includedirs includedirs
{ {
"include", path.join(scriptdir, "include"),
"lib", path.join(scriptdir, "lib"),
"../ogg/include"
} }
files files
{ {
"lib/envelope.h", path.join(scriptdir, "lib/envelope.h"),
"lib/lpc.h", path.join(scriptdir, "lib/lpc.h"),
"lib/lsp.h", path.join(scriptdir, "lib/lsp.h"),
"lib/codebook.h", path.join(scriptdir, "lib/codebook.h"),
"lib/misc.h", path.join(scriptdir, "lib/misc.h"),
"lib/psy.h", path.join(scriptdir, "lib/psy.h"),
"lib/masking.h", path.join(scriptdir, "lib/masking.h"),
"lib/os.h", path.join(scriptdir, "lib/os.h"),
"lib/mdct.h", path.join(scriptdir, "lib/mdct.h"),
"lib/smallft.h", path.join(scriptdir, "lib/smallft.h"),
"lib/highlevel.h", path.join(scriptdir, "lib/highlevel.h"),
"lib/registry.h", path.join(scriptdir, "lib/registry.h"),
"lib/scales.h", path.join(scriptdir, "lib/scales.h"),
"lib/window.h", path.join(scriptdir, "lib/window.h"),
"lib/lookup.h", path.join(scriptdir, "lib/lookup.h"),
"lib/lookup_data.h", path.join(scriptdir, "lib/lookup_data.h"),
"lib/codec_internal.h", path.join(scriptdir, "lib/codec_internal.h"),
"lib/backends.h", path.join(scriptdir, "lib/backends.h"),
"lib/bitrate.h", path.join(scriptdir, "lib/bitrate.h"),
"lib/mdct.c", path.join(scriptdir, "lib/mdct.c"),
"lib/smallft.c", path.join(scriptdir, "lib/smallft.c"),
"lib/block.c", path.join(scriptdir, "lib/block.c"),
"lib/envelope.c", path.join(scriptdir, "lib/envelope.c"),
"lib/window.c", path.join(scriptdir, "lib/window.c"),
"lib/lsp.c", path.join(scriptdir, "lib/lsp.c"),
"lib/lpc.c", path.join(scriptdir, "lib/lpc.c"),
"lib/analysis.c", path.join(scriptdir, "lib/analysis.c"),
"lib/synthesis.c", path.join(scriptdir, "lib/synthesis.c"),
"lib/psy.c", path.join(scriptdir, "lib/psy.c"),
"lib/info.c", path.join(scriptdir, "lib/info.c"),
"lib/floor1.c", path.join(scriptdir, "lib/floor1.c"),
"lib/floor0.c", path.join(scriptdir, "lib/floor0.c"),
"lib/res0.c", path.join(scriptdir, "lib/res0.c"),
"lib/mapping0.c", path.join(scriptdir, "lib/mapping0.c"),
"lib/registry.c", path.join(scriptdir, "lib/registry.c"),
"lib/codebook.c", path.join(scriptdir, "lib/codebook.c"),
"lib/sharedbook.c", path.join(scriptdir, "lib/sharedbook.c"),
"lib/lookup.c", path.join(scriptdir, "lib/lookup.c"),
"lib/bitrate.c", path.join(scriptdir, "lib/bitrate.c"),
"lib/vorbisfile.c", path.join(scriptdir, "lib/vorbisfile.c"),
"lib/vorbisenc.c", path.join(scriptdir, "lib/vorbisenc.c"),
} }
filter "configurations:Debug"
runtime "Debug"
symbols "on"
filter "configurations:Release"
runtime "Release"
optimize "Speed"
filter"" filter""
end
function m.link()
links {"vorbis", "ogg"}
externalincludedirs
{
path.join(scriptdir, "include"),
path.join(scriptdir, "lib"),
path.join(scriptdir, "../ogg/include")
}
end
return m