IpAddress is always valid

This commit is contained in:
Vittorio Romeo 2022-06-27 12:02:35 +02:00
parent fd3526f742
commit 8c8d97c6c9
16 changed files with 150 additions and 235 deletions

View File

@ -5,6 +5,7 @@
#include <SFML/Network.hpp> #include <SFML/Network.hpp>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <optional>
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -26,17 +27,17 @@ std::ostream& operator <<(std::ostream& stream, const sf::Ftp::Response& respons
int main() int main()
{ {
// Choose the server address // Choose the server address
sf::IpAddress address; std::optional<sf::IpAddress> address;
do do
{ {
std::cout << "Enter the FTP server address: "; std::cout << "Enter the FTP server address: ";
std::cin >> address; std::cin >> address;
} }
while (address == sf::IpAddress::None); while (!address.has_value());
// Connect to the server // Connect to the server
sf::Ftp server; sf::Ftp server;
sf::Ftp::Response connectResponse = server.connect(address); sf::Ftp::Response connectResponse = server.connect(address.value());
std::cout << connectResponse << std::endl; std::cout << connectResponse << std::endl;
if (!connectResponse.isOk()) if (!connectResponse.isOk())
return EXIT_FAILURE; return EXIT_FAILURE;

View File

@ -26,7 +26,7 @@ void runTcpServer(unsigned short port)
sf::TcpSocket socket; sf::TcpSocket socket;
if (listener.accept(socket) != sf::Socket::Done) if (listener.accept(socket) != sf::Socket::Done)
return; return;
std::cout << "Client connected: " << socket.getRemoteAddress() << std::endl; std::cout << "Client connected: " << socket.getRemoteAddress().value() << std::endl;
// Send a message to the connected client // Send a message to the connected client
const char out[] = "Hi, I'm the server"; const char out[] = "Hi, I'm the server";
@ -51,21 +51,21 @@ void runTcpServer(unsigned short port)
void runTcpClient(unsigned short port) void runTcpClient(unsigned short port)
{ {
// Ask for the server address // Ask for the server address
sf::IpAddress server; std::optional<sf::IpAddress> server;
do do
{ {
std::cout << "Type the address or name of the server to connect to: "; std::cout << "Type the address or name of the server to connect to: ";
std::cin >> server; std::cin >> server;
} }
while (server == sf::IpAddress::None); while (!server.has_value());
// Create a socket for communicating with the server // Create a socket for communicating with the server
sf::TcpSocket socket; sf::TcpSocket socket;
// Connect to the server // Connect to the server
if (socket.connect(server, port) != sf::Socket::Done) if (socket.connect(server.value(), port) != sf::Socket::Done)
return; return;
std::cout << "Connected to server " << server << std::endl; std::cout << "Connected to server " << server.value() << std::endl;
// Receive a message from the server // Receive a message from the server
char in[128]; char in[128];

View File

@ -5,6 +5,7 @@
#include <SFML/Network.hpp> #include <SFML/Network.hpp>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <optional>
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -24,15 +25,15 @@ void runUdpServer(unsigned short port)
// Wait for a message // Wait for a message
char in[128]; char in[128];
std::size_t received; std::size_t received;
sf::IpAddress sender; std::optional<sf::IpAddress> sender;
unsigned short senderPort; unsigned short senderPort;
if (socket.receive(in, sizeof(in), received, sender, senderPort) != sf::Socket::Done) if (socket.receive(in, sizeof(in), received, sender, senderPort) != sf::Socket::Done)
return; return;
std::cout << "Message received from client " << sender << ": " << std::quoted(in) << std::endl; std::cout << "Message received from client " << sender.value() << ": " << std::quoted(in) << std::endl;
// Send an answer to the client // Send an answer to the client
const char out[] = "Hi, I'm the server"; const char out[] = "Hi, I'm the server";
if (socket.send(out, sizeof(out), sender, senderPort) != sf::Socket::Done) if (socket.send(out, sizeof(out), sender.value(), senderPort) != sf::Socket::Done)
return; return;
std::cout << "Message sent to the client: " << std::quoted(out) << std::endl; std::cout << "Message sent to the client: " << std::quoted(out) << std::endl;
} }
@ -45,29 +46,29 @@ void runUdpServer(unsigned short port)
void runUdpClient(unsigned short port) void runUdpClient(unsigned short port)
{ {
// Ask for the server address // Ask for the server address
sf::IpAddress server; std::optional<sf::IpAddress> server;
do do
{ {
std::cout << "Type the address or name of the server to connect to: "; std::cout << "Type the address or name of the server to connect to: ";
std::cin >> server; std::cin >> server;
} }
while (server == sf::IpAddress::None); while (!server.has_value());
// Create a socket for communicating with the server // Create a socket for communicating with the server
sf::UdpSocket socket; sf::UdpSocket socket;
// Send a message to the server // Send a message to the server
const char out[] = "Hi, I'm a client"; const char out[] = "Hi, I'm a client";
if (socket.send(out, sizeof(out), server, port) != sf::Socket::Done) if (socket.send(out, sizeof(out), server.value(), port) != sf::Socket::Done)
return; return;
std::cout << "Message sent to the server: " << std::quoted(out) << std::endl; std::cout << "Message sent to the server: " << std::quoted(out) << std::endl;
// Receive an answer from anyone (but most likely from the server) // Receive an answer from anyone (but most likely from the server)
char in[128]; char in[128];
std::size_t received; std::size_t received;
sf::IpAddress sender; std::optional<sf::IpAddress> sender;
unsigned short senderPort; unsigned short senderPort;
if (socket.receive(in, sizeof(in), received, sender, senderPort) != sf::Socket::Done) if (socket.receive(in, sizeof(in), received, sender, senderPort) != sf::Socket::Done)
return; return;
std::cout << "Message received from " << sender << ": " << std::quoted(in) << std::endl; std::cout << "Message received from " << sender.value() << ": " << std::quoted(in) << std::endl;
} }

View File

@ -121,16 +121,16 @@ void doClient(unsigned short port)
} }
// Ask for server address // Ask for server address
sf::IpAddress server; std::optional<sf::IpAddress> server;
do do
{ {
std::cout << "Type address or name of the server to connect to: "; std::cout << "Type address or name of the server to connect to: ";
std::cin >> server; std::cin >> server;
} }
while (server == sf::IpAddress::None); while (!server.has_value());
// Create an instance of our custom recorder // Create an instance of our custom recorder
NetworkRecorder recorder(server, port); NetworkRecorder recorder(server.value(), port);
// Wait for user input... // Wait for user input...
std::cin.ignore(10000, '\n'); std::cin.ignore(10000, '\n');

View File

@ -50,7 +50,7 @@ public:
// Wait for a connection // Wait for a connection
if (m_listener.accept(m_client) != sf::Socket::Done) if (m_listener.accept(m_client) != sf::Socket::Done)
return; return;
std::cout << "Client connected: " << m_client.getRemoteAddress() << std::endl; std::cout << "Client connected: " << m_client.getRemoteAddress().value() << std::endl;
// Start playback // Start playback
play(); play();

View File

@ -33,6 +33,7 @@
#include <SFML/Network/TcpSocket.hpp> #include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
#include <map> #include <map>
#include <optional>
#include <string> #include <string>
@ -420,10 +421,10 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
TcpSocket m_connection; //!< Connection to the host TcpSocket m_connection; //!< Connection to the host
IpAddress m_host; //!< Web host address std::optional<IpAddress> m_host; //!< Web host address
std::string m_hostName; //!< Web host name std::string m_hostName; //!< Web host name
unsigned short m_port; //!< Port used for connection with host unsigned short m_port; //!< Port used for connection with host
}; };
} // namespace sf } // namespace sf

View File

@ -32,6 +32,7 @@
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
#include <iosfwd> #include <iosfwd>
#include <optional> #include <optional>
#include <string_view>
#include <string> #include <string>
@ -44,17 +45,8 @@ namespace sf
class SFML_NETWORK_API IpAddress class SFML_NETWORK_API IpAddress
{ {
public: public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Default constructor /// \brief Construct the address from a null-terminated string view
///
/// This constructor creates an empty (invalid) address
///
////////////////////////////////////////////////////////////
IpAddress();
////////////////////////////////////////////////////////////
/// \brief Construct the address from a string
/// ///
/// Here \a address can be either a decimal address /// Here \a address can be either a decimal address
/// (ex: "192.168.1.56") or a network name (ex: "localhost"). /// (ex: "192.168.1.56") or a network name (ex: "localhost").
@ -62,28 +54,14 @@ public:
/// \param address IP address or network name /// \param address IP address or network name
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress(const std::string& address); static std::optional<IpAddress> resolve(std::string_view address);
////////////////////////////////////////////////////////////
/// \brief Construct the address from a string
///
/// Here \a address can be either a decimal address
/// (ex: "192.168.1.56") or a network name (ex: "localhost").
/// This is equivalent to the constructor taking a std::string
/// parameter, it is defined for convenience so that the
/// implicit conversions from literal strings to IpAddress work.
///
/// \param address IP address or network name
///
////////////////////////////////////////////////////////////
IpAddress(const char* address);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Construct the address from 4 bytes /// \brief Construct the address from 4 bytes
/// ///
/// Calling IpAddress(a, b, c, d) is equivalent to calling /// Calling IpAddress(a, b, c, d) is equivalent to calling
/// IpAddress("a.b.c.d"), but safer as it doesn't have to /// IpAddress::resolve("a.b.c.d"), but safer as it doesn't
/// parse a string to get the address components. /// have to parse a string to get the address components.
/// ///
/// \param byte0 First byte of the address /// \param byte0 First byte of the address
/// \param byte1 Second byte of the address /// \param byte1 Second byte of the address
@ -152,7 +130,7 @@ public:
/// \see getPublicAddress /// \see getPublicAddress
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static IpAddress getLocalAddress(); static std::optional<IpAddress> getLocalAddress();
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the computer's public address /// \brief Get the computer's public address
@ -176,12 +154,11 @@ public:
/// \see getLocalAddress /// \see getLocalAddress
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static IpAddress getPublicAddress(Time timeout = Time::Zero); static std::optional<IpAddress> getPublicAddress(Time timeout = Time::Zero);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Static member data // Static member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
static const IpAddress None; //!< Value representing an empty/invalid address
static const IpAddress Any; //!< Value representing any address (0.0.0.0) 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 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) static const IpAddress Broadcast; //!< The "broadcast" address (for sending UDP messages to everyone on a local network)
@ -193,7 +170,7 @@ private:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Member data // Member data
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::optional<Uint32> m_address; //!< Address stored as an unsigned 32 bits integer Uint32 m_address; //!< Address stored as an unsigned 32 bits integer
}; };
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@ -271,7 +248,7 @@ SFML_NETWORK_API bool operator >=(const IpAddress& left, const IpAddress& right)
/// \return Reference to the input stream /// \return Reference to the input stream
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
SFML_NETWORK_API std::istream& operator >>(std::istream& stream, IpAddress& address); 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 /// \brief Overload of << operator to print an IP address to an output stream
@ -301,16 +278,14 @@ SFML_NETWORK_API std::ostream& operator <<(std::ostream& stream, const IpAddress
/// ///
/// Usage example: /// Usage example:
/// \code /// \code
/// sf::IpAddress a0; // an invalid address /// auto a2 = sf::IpAddress::resolve("127.0.0.1"); // the local host address
/// sf::IpAddress a1 = sf::IpAddress::None; // an invalid address (same as a0) /// auto a3 = sf::IpAddress::Broadcast; // the broadcast address
/// sf::IpAddress a2("127.0.0.1"); // the local host address /// sf::IpAddress a4(192, 168, 1, 56); // a local address
/// sf::IpAddress a3 = sf::IpAddress::Broadcast; // the broadcast address /// auto a5 = sf::IpAddress::resolve("my_computer"); // a local address created from a network name
/// sf::IpAddress a4(192, 168, 1, 56); // a local address /// auto a6 = sf::IpAddress::resolve("89.54.1.169"); // a distant address
/// sf::IpAddress a5("my_computer"); // a local address created from a network name /// auto a7 = sf::IpAddress::resolve("www.google.com"); // a distant address created from a network name
/// sf::IpAddress a6("89.54.1.169"); // a distant address /// auto a8 = sf::IpAddress::getLocalAddress(); // my address on the local network
/// sf::IpAddress a7("www.google.com"); // a distant address created from a network name /// auto a9 = sf::IpAddress::getPublicAddress(); // my address on the internet
/// sf::IpAddress a8 = sf::IpAddress::getLocalAddress(); // my address on the local network
/// sf::IpAddress a9 = sf::IpAddress::getPublicAddress(); // my address on the internet
/// \endcode /// \endcode
/// ///
/// Note that sf::IpAddress currently doesn't support IPv6 /// Note that sf::IpAddress currently doesn't support IPv6

View File

@ -159,7 +159,7 @@ public:
/// if (listener.accept(client) == sf::Socket::Done) /// if (listener.accept(client) == sf::Socket::Done)
/// { /// {
/// // A new client just connected! /// // A new client just connected!
/// std::cout << "New connection received from " << client.getRemoteAddress() << std::endl; /// std::cout << "New connection received from " << client.getRemoteAddress().value() << std::endl;
/// doSomethingWith(client); /// doSomethingWith(client);
/// } /// }
/// } /// }

View File

@ -31,6 +31,7 @@
#include <SFML/Network/Export.hpp> #include <SFML/Network/Export.hpp>
#include <SFML/Network/Socket.hpp> #include <SFML/Network/Socket.hpp>
#include <SFML/System/Time.hpp> #include <SFML/System/Time.hpp>
#include <optional>
namespace sf namespace sf
@ -69,14 +70,14 @@ public:
/// \brief Get the address of the connected peer /// \brief Get the address of the connected peer
/// ///
/// If the socket is not connected, this function returns /// If the socket is not connected, this function returns
/// sf::IpAddress::None. /// an unset optional.
/// ///
/// \return Address of the remote peer /// \return Address of the remote peer
/// ///
/// \see getRemotePort /// \see getRemotePort
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress getRemoteAddress() const; std::optional<IpAddress> getRemoteAddress() const;
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
/// \brief Get the port of the connected peer to which /// \brief Get the port of the connected peer to which
@ -299,7 +300,7 @@ private:
/// // Wait for a connection /// // Wait for a connection
/// sf::TcpSocket socket; /// sf::TcpSocket socket;
/// listener.accept(socket); /// listener.accept(socket);
/// std::cout << "New client connected: " << socket.getRemoteAddress() << std::endl; /// std::cout << "New client connected: " << socket.getRemoteAddress().value() << std::endl;
/// ///
/// // Receive a message from the client /// // Receive a message from the client
/// char buffer[1024]; /// char buffer[1024];

View File

@ -31,6 +31,7 @@
#include <SFML/Network/Export.hpp> #include <SFML/Network/Export.hpp>
#include <SFML/Network/Socket.hpp> #include <SFML/Network/Socket.hpp>
#include <SFML/Network/IpAddress.hpp> #include <SFML/Network/IpAddress.hpp>
#include <optional>
#include <vector> #include <vector>
@ -152,7 +153,7 @@ public:
/// \see send /// \see send
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data, std::size_t size, std::size_t& received, IpAddress& remoteAddress, unsigned short& remotePort); [[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 /// \brief Send a formatted packet of data to a remote peer
@ -187,7 +188,7 @@ public:
/// \see send /// \see send
/// ///
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet, IpAddress& remoteAddress, unsigned short& remotePort); [[nodiscard]] Status receive(Packet& packet, std::optional<IpAddress>& remoteAddress, unsigned short& remotePort);
private: private:
@ -263,10 +264,10 @@ private:
/// // Receive an answer (most likely from 192.168.1.50, but could be anyone else) /// // Receive an answer (most likely from 192.168.1.50, but could be anyone else)
/// char buffer[1024]; /// char buffer[1024];
/// std::size_t received = 0; /// std::size_t received = 0;
/// sf::IpAddress sender; /// std::optional<sf::IpAddress> sender;
/// unsigned short port; /// unsigned short port;
/// socket.receive(buffer, sizeof(buffer), received, sender, port); /// if (socket.receive(buffer, sizeof(buffer), received, sender, port) == sf::Socket::Done)
/// std::cout << sender.ToString() << " said: " << buffer << std::endl; /// std::cout << sender->toString() << " said: " << buffer << std::endl;
/// ///
/// // ----- The server ----- /// // ----- The server -----
/// ///
@ -277,10 +278,10 @@ private:
/// // Receive a message from anyone /// // Receive a message from anyone
/// char buffer[1024]; /// char buffer[1024];
/// std::size_t received = 0; /// std::size_t received = 0;
/// sf::IpAddress sender; /// std::optional<sf::IpAddress> sender;
/// unsigned short port; /// unsigned short port;
/// socket.receive(buffer, sizeof(buffer), received, sender, port); /// if (socket.receive(buffer, sizeof(buffer), received, sender, port) == sf::Socket::Done)
/// std::cout << sender.ToString() << " said: " << buffer << std::endl; /// std::cout << sender->toString() << " said: " << buffer << std::endl;
/// ///
/// // Send an answer /// // Send an answer
/// std::string message = "Welcome " + sender.toString(); /// std::string message = "Welcome " + sender.toString();

View File

@ -329,7 +329,7 @@ void Http::setHost(const std::string& host, unsigned short port)
if (!m_hostName.empty() && (*m_hostName.rbegin() == '/')) if (!m_hostName.empty() && (*m_hostName.rbegin() == '/'))
m_hostName.erase(m_hostName.size() - 1); m_hostName.erase(m_hostName.size() - 1);
m_host = IpAddress(m_hostName); m_host = IpAddress::resolve(m_hostName);
} }
@ -369,7 +369,7 @@ Http::Response Http::sendRequest(const Http::Request& request, Time timeout)
Response received; Response received;
// Connect the socket to the host // Connect the socket to the host
if (m_connection.connect(m_host, m_port, timeout) == Socket::Done) if (m_connection.connect(m_host.value(), m_port, timeout) == Socket::Done)
{ {
// Convert the request to string and send it through the connected socket // Convert the request to string and send it through the connected socket
std::string requestStr = toSend.prepare(); std::string requestStr = toSend.prepare();

View File

@ -37,68 +37,56 @@
namespace sf namespace sf
{ {
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
const IpAddress IpAddress::None;
const IpAddress IpAddress::Any(0, 0, 0, 0); const IpAddress IpAddress::Any(0, 0, 0, 0);
const IpAddress IpAddress::LocalHost(127, 0, 0, 1); const IpAddress IpAddress::LocalHost(127, 0, 0, 1);
const IpAddress IpAddress::Broadcast(255, 255, 255, 255); const IpAddress IpAddress::Broadcast(255, 255, 255, 255);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress::IpAddress() = default; std::optional<IpAddress> IpAddress::resolve(std::string_view address)
////////////////////////////////////////////////////////////
IpAddress::IpAddress(const std::string& address)
{ {
if (address == "255.255.255.255") using namespace std::string_view_literals;
if (address.empty())
return std::nullopt;
if (address == "255.255.255.255"sv)
{ {
// The broadcast address needs to be handled explicitly, // The broadcast address needs to be handled explicitly,
// because it is also the value returned by inet_addr on error // because it is also the value returned by inet_addr on error
m_address = INADDR_BROADCAST; return Broadcast;
return;
} }
if (address == "0.0.0.0") if (address == "0.0.0.0"sv)
{ return Any;
m_address = INADDR_ANY;
return;
}
// Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx") // Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx")
sf::Uint32 ip = inet_addr(address.c_str()); if (const Uint32 ip = inet_addr(address.data()); ip != INADDR_NONE)
if (ip != INADDR_NONE) return IpAddress(ntohl(ip));
{
m_address = ip;
return;
}
// Not a valid address, try to convert it as a host name // Not a valid address, try to convert it as a host name
addrinfo hints; addrinfo hints{}; // Zero-initialize
std::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
addrinfo* result = nullptr; addrinfo* result = nullptr;
if (getaddrinfo(address.c_str(), nullptr, &hints, &result) == 0 && result) if (getaddrinfo(address.data(), nullptr, &hints, &result) == 0 && result != nullptr)
{ {
sockaddr_in sin; sockaddr_in sin;
std::memcpy(&sin, result->ai_addr, sizeof(*result->ai_addr)); std::memcpy(&sin, result->ai_addr, sizeof(*result->ai_addr));
ip = sin.sin_addr.s_addr;
const Uint32 ip = sin.sin_addr.s_addr;
freeaddrinfo(result); freeaddrinfo(result);
m_address = ip;
return; return IpAddress(ntohl(ip));
} }
}
return std::nullopt;
////////////////////////////////////////////////////////////
IpAddress::IpAddress(const char* address) :
IpAddress(std::string(address))
{
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress::IpAddress(Uint8 byte0, Uint8 byte1, Uint8 byte2, Uint8 byte3) : IpAddress::IpAddress(Uint8 byte0, Uint8 byte1, Uint8 byte2, Uint8 byte3) :
m_address(htonl(static_cast<uint32_t>((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3))) m_address(htonl(static_cast<std::uint32_t>((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3)))
{ {
} }
@ -114,7 +102,7 @@ m_address(htonl(address))
std::string IpAddress::toString() const std::string IpAddress::toString() const
{ {
in_addr address; in_addr address;
address.s_addr = m_address.value_or(0); address.s_addr = m_address;
return inet_ntoa(address); return inet_ntoa(address);
} }
@ -123,30 +111,28 @@ std::string IpAddress::toString() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Uint32 IpAddress::toInteger() const Uint32 IpAddress::toInteger() const
{ {
return ntohl(m_address.value_or(0)); return ntohl(m_address);
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress IpAddress::getLocalAddress() std::optional<IpAddress> IpAddress::getLocalAddress()
{ {
// The method here is to connect a UDP socket to anyone (here to localhost), // The method here is to connect a UDP socket to anyone (here to localhost),
// and get the local socket address with the getsockname function. // 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. // UDP connection will not send anything to the network, so this function won't cause any overhead.
IpAddress localAddress;
// Create the socket // Create the socket
SocketHandle sock = socket(PF_INET, SOCK_DGRAM, 0); SocketHandle sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == priv::SocketImpl::invalidSocket()) if (sock == priv::SocketImpl::invalidSocket())
return localAddress; return std::nullopt;
// Connect the socket to localhost on any port // Connect the socket to localhost on any port
sockaddr_in address = priv::SocketImpl::createAddress(ntohl(INADDR_LOOPBACK), 9); sockaddr_in address = priv::SocketImpl::createAddress(ntohl(INADDR_LOOPBACK), 9);
if (connect(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1) if (connect(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) == -1)
{ {
priv::SocketImpl::close(sock); priv::SocketImpl::close(sock);
return localAddress; return std::nullopt;
} }
// Get the local address of the socket connection // Get the local address of the socket connection
@ -154,21 +140,19 @@ IpAddress IpAddress::getLocalAddress()
if (getsockname(sock, reinterpret_cast<sockaddr*>(&address), &size) == -1) if (getsockname(sock, reinterpret_cast<sockaddr*>(&address), &size) == -1)
{ {
priv::SocketImpl::close(sock); priv::SocketImpl::close(sock);
return localAddress; return std::nullopt;
} }
// Close the socket // Close the socket
priv::SocketImpl::close(sock); priv::SocketImpl::close(sock);
// Finally build the IP address // Finally build the IP address
localAddress = IpAddress(ntohl(address.sin_addr.s_addr)); return IpAddress(ntohl(address.sin_addr.s_addr));
return localAddress;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress IpAddress::getPublicAddress(Time timeout) std::optional<IpAddress> IpAddress::getPublicAddress(Time timeout)
{ {
// The trick here is more complicated, because the only way // The trick here is more complicated, because the only way
// to get our public IP address is to get it from a distant computer. // to get our public IP address is to get it from a distant computer.
@ -180,10 +164,10 @@ IpAddress IpAddress::getPublicAddress(Time timeout)
Http::Request request("/ip-provider.php", Http::Request::Get); Http::Request request("/ip-provider.php", Http::Request::Get);
Http::Response page = server.sendRequest(request, timeout); Http::Response page = server.sendRequest(request, timeout);
if (page.getStatus() == Http::Response::Ok) if (page.getStatus() == Http::Response::Ok)
return IpAddress(page.getBody()); return IpAddress::resolve(page.getBody());
// Something failed: return an invalid address // Something failed: return an invalid address
return IpAddress(); return std::nullopt;
} }
@ -230,11 +214,11 @@ bool operator >=(const IpAddress& left, const IpAddress& right)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
std::istream& operator >>(std::istream& stream, IpAddress& address) std::istream& operator >>(std::istream& stream, std::optional<IpAddress>& address)
{ {
std::string str; std::string str;
stream >> str; stream >> str;
address = IpAddress(str); address = IpAddress::resolve(str);
return stream; return stream;
} }

View File

@ -71,7 +71,7 @@ Socket::Status TcpListener::listen(unsigned short port, const IpAddress& address
create(); create();
// Check if the address is valid // Check if the address is valid
if ((address == IpAddress::None) || (address == IpAddress::Broadcast)) if (address == IpAddress::Broadcast)
return Error; return Error;
// Bind the socket to the specified port // Bind the socket to the specified port

View File

@ -79,7 +79,7 @@ unsigned short TcpSocket::getLocalPort() const
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
IpAddress TcpSocket::getRemoteAddress() const std::optional<IpAddress> TcpSocket::getRemoteAddress() const
{ {
if (getHandle() != priv::SocketImpl::invalidSocket()) if (getHandle() != priv::SocketImpl::invalidSocket())
{ {
@ -93,7 +93,7 @@ IpAddress TcpSocket::getRemoteAddress() const
} }
// We failed to retrieve the address // We failed to retrieve the address
return IpAddress::None; return std::nullopt;
} }
@ -183,7 +183,7 @@ Socket::Status TcpSocket::connect(const IpAddress& remoteAddress, unsigned short
{ {
// At this point the connection may have been either accepted or refused. // 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 // To know whether it's a success or a failure, we must check the address of the connected peer
if (getRemoteAddress() != IpAddress::None) if (getRemoteAddress().has_value())
{ {
// Connection accepted // Connection accepted
status = Done; status = Done;

View File

@ -74,7 +74,7 @@ Socket::Status UdpSocket::bind(unsigned short port, const IpAddress& address)
create(); create();
// Check if the address is valid // Check if the address is valid
if ((address == IpAddress::None) || (address == IpAddress::Broadcast)) if (address == IpAddress::Broadcast)
return Error; return Error;
// Bind the socket // Bind the socket
@ -129,11 +129,11 @@ Socket::Status UdpSocket::send(const void* data, std::size_t size, const IpAddre
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Socket::Status UdpSocket::receive(void* data, std::size_t size, std::size_t& received, IpAddress& remoteAddress, unsigned short& remotePort) 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 // First clear the variables to fill
received = 0; received = 0;
remoteAddress = IpAddress(); remoteAddress = std::nullopt;
remotePort = 0; remotePort = 0;
// Check the destination buffer // Check the destination buffer
@ -187,7 +187,7 @@ Socket::Status UdpSocket::send(Packet& packet, const IpAddress& remoteAddress, u
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
Socket::Status UdpSocket::receive(Packet& packet, IpAddress& remoteAddress, unsigned short& remotePort) Socket::Status UdpSocket::receive(Packet& packet, std::optional<IpAddress>& remoteAddress, unsigned short& remotePort)
{ {
// See the detailed comment in send(Packet) above. // See the detailed comment in send(Packet) above.

View File

@ -3,78 +3,45 @@
#include <doctest.h> #include <doctest.h>
#include <sstream> #include <sstream>
#include <string_view>
using namespace std::string_literals; using namespace std::string_literals;
using namespace std::string_view_literals;
TEST_CASE("sf::IpAddress class - [network]") TEST_CASE("sf::IpAddress class - [network]")
{ {
SUBCASE("Construction") SUBCASE("Construction")
{ {
SUBCASE("Default constructor") SUBCASE("static 'create' function")
{ {
const sf::IpAddress ipAddress; const auto ipAddress = sf::IpAddress::resolve("192.168.0.1"sv);
CHECK(ipAddress.toString() == "0.0.0.0"s); REQUIRE(ipAddress.has_value());
CHECK(ipAddress.toInteger() == 0); CHECK(ipAddress->toString() == "192.168.0.1"s);
CHECK(ipAddress == sf::IpAddress::None); CHECK(ipAddress->toInteger() == 0xC0A80001);
} CHECK(*ipAddress != sf::IpAddress::Any);
CHECK(*ipAddress != sf::IpAddress::Broadcast);
CHECK(*ipAddress != sf::IpAddress::LocalHost);
SUBCASE("std::string constructor") const auto broadcast = sf::IpAddress::resolve("255.255.255.255"sv);
{ REQUIRE(broadcast.has_value());
const sf::IpAddress ipAddress = "192.168.0.1"s; CHECK(broadcast->toString() == "255.255.255.255"s);
CHECK(ipAddress.toString() == "192.168.0.1"s); CHECK(broadcast->toInteger() == 0xFFFFFFFF);
CHECK(ipAddress.toInteger() == 0xC0A80001); CHECK(*broadcast == sf::IpAddress::Broadcast);
CHECK(ipAddress != sf::IpAddress::None);
CHECK(ipAddress != sf::IpAddress::Any);
CHECK(ipAddress != sf::IpAddress::Broadcast);
CHECK(ipAddress != sf::IpAddress::LocalHost);
const sf::IpAddress broadcast("255.255.255.255"s); const auto any = sf::IpAddress::resolve("0.0.0.0"sv);
CHECK(broadcast.toString() == "255.255.255.255"s); REQUIRE(any.has_value());
CHECK(broadcast.toInteger() == 0xFFFFFFFF); CHECK(any->toString() == "0.0.0.0"s);
CHECK(broadcast == sf::IpAddress::Broadcast); CHECK(any->toInteger() == 0x00000000);
CHECK(*any == sf::IpAddress::Any);
const sf::IpAddress any("0.0.0.0"s); const auto localHost = sf::IpAddress::resolve("localhost"s);
CHECK(any.toString() == "0.0.0.0"s); REQUIRE(localHost.has_value());
CHECK(any.toInteger() == 0x00000000); CHECK(localHost->toString() == "127.0.0.1"s);
CHECK(any == sf::IpAddress::Any); CHECK(localHost->toInteger() == 0x7F000001);
CHECK(*localHost == sf::IpAddress::LocalHost);
const sf::IpAddress localHost("localhost"s); CHECK(!sf::IpAddress::resolve("255.255.255.256"s).has_value());
CHECK(localHost.toString() == "127.0.0.1"s); CHECK(!sf::IpAddress::resolve("").has_value());
CHECK(localHost.toInteger() == 0x7F000001);
CHECK(localHost == sf::IpAddress::LocalHost);
const sf::IpAddress invalidIpAddress("255.255.255.256"s);
CHECK(invalidIpAddress.toString() == "0.0.0.0"s);
CHECK(invalidIpAddress.toInteger() == 0);
CHECK(invalidIpAddress == sf::IpAddress::None);
}
SUBCASE("const char* constructor")
{
const sf::IpAddress ipAddress = "192.168.0.1";
CHECK(ipAddress.toString() == "192.168.0.1"s);
CHECK(ipAddress.toInteger() == 0xC0A80001);
CHECK(ipAddress != sf::IpAddress::None);
const sf::IpAddress broadcast("255.255.255.255");
CHECK(broadcast.toString() == "255.255.255.255"s);
CHECK(broadcast.toInteger() == 0xFFFFFFFF);
CHECK(broadcast == sf::IpAddress::Broadcast);
const sf::IpAddress any("0.0.0.0");
CHECK(any.toString() == "0.0.0.0"s);
CHECK(any.toInteger() == 0x00000000);
CHECK(any == sf::IpAddress::Any);
const sf::IpAddress localHost("localhost");
CHECK(localHost.toString() == "127.0.0.1"s);
CHECK(localHost.toInteger() == 0x7F000001);
CHECK(localHost == sf::IpAddress::LocalHost);
const sf::IpAddress invalidIpAddress("255.255.255.256");
CHECK(invalidIpAddress.toString() == "0.0.0.0"s);
CHECK(invalidIpAddress.toInteger() == 0);
CHECK(invalidIpAddress == sf::IpAddress::None);
} }
SUBCASE("Byte constructor") SUBCASE("Byte constructor")
@ -82,7 +49,6 @@ TEST_CASE("sf::IpAddress class - [network]")
const sf::IpAddress ipAddress(142, 250, 69, 238); const sf::IpAddress ipAddress(142, 250, 69, 238);
CHECK(ipAddress.toString() == "142.250.69.238"s); CHECK(ipAddress.toString() == "142.250.69.238"s);
CHECK(ipAddress.toInteger() == 0x8EFA45EE); CHECK(ipAddress.toInteger() == 0x8EFA45EE);
CHECK(ipAddress != sf::IpAddress::None);
} }
SUBCASE("Uint32 constructor") SUBCASE("Uint32 constructor")
@ -90,7 +56,6 @@ TEST_CASE("sf::IpAddress class - [network]")
const sf::IpAddress ipAddress(0xDEADBEEF); const sf::IpAddress ipAddress(0xDEADBEEF);
CHECK(ipAddress.toString() == "222.173.190.239"s); CHECK(ipAddress.toString() == "222.173.190.239"s);
CHECK(ipAddress.toInteger() == 0xDEADBEEF); CHECK(ipAddress.toInteger() == 0xDEADBEEF);
CHECK(ipAddress != sf::IpAddress::None);
} }
} }
@ -98,26 +63,23 @@ TEST_CASE("sf::IpAddress class - [network]")
{ {
SUBCASE("getLocalAddress") SUBCASE("getLocalAddress")
{ {
const sf::IpAddress ipAddress = sf::IpAddress::getLocalAddress(); const std::optional<sf::IpAddress> ipAddress = sf::IpAddress::getLocalAddress();
CHECK(ipAddress.toString() != "0.0.0.0"); REQUIRE(ipAddress.has_value());
CHECK(ipAddress.toInteger() != 0); CHECK(ipAddress->toString() != "0.0.0.0");
CHECK(ipAddress != sf::IpAddress::None); CHECK(ipAddress->toInteger() != 0);
} }
SUBCASE("getPublicAddress") SUBCASE("getPublicAddress")
{ {
const sf::IpAddress ipAddress = sf::IpAddress::getPublicAddress(); const std::optional<sf::IpAddress> ipAddress = sf::IpAddress::getPublicAddress();
CHECK(ipAddress.toString() != "0.0.0.0"); REQUIRE(ipAddress.has_value());
CHECK(ipAddress.toInteger() != 0); CHECK(ipAddress->toString() != "0.0.0.0");
CHECK(ipAddress != sf::IpAddress::None); CHECK(ipAddress->toInteger() != 0);
} }
} }
SUBCASE("Static constants") SUBCASE("Static constants")
{ {
CHECK(sf::IpAddress::None.toString() == "0.0.0.0"s);
CHECK(sf::IpAddress::None.toInteger() == 0);
CHECK(sf::IpAddress::Any.toString() == "0.0.0.0"s); CHECK(sf::IpAddress::Any.toString() == "0.0.0.0"s);
CHECK(sf::IpAddress::Any.toInteger() == 0); CHECK(sf::IpAddress::Any.toInteger() == 0);
@ -132,24 +94,19 @@ TEST_CASE("sf::IpAddress class - [network]")
{ {
SUBCASE("operator==") SUBCASE("operator==")
{ {
CHECK(sf::IpAddress() == sf::IpAddress());
CHECK(sf::IpAddress("") == sf::IpAddress(""s));
CHECK(sf::IpAddress("8.8.8.8") == sf::IpAddress(8, 8, 8, 8));
CHECK(sf::IpAddress(0x42, 0x69, 0x96, 0x24) == sf::IpAddress(0x42699624)); CHECK(sf::IpAddress(0x42, 0x69, 0x96, 0x24) == sf::IpAddress(0x42699624));
CHECK(sf::IpAddress(0xABCDEF01) == sf::IpAddress("171.205.239.1")); CHECK(sf::IpAddress(0xABCDEF01) == sf::IpAddress(171, 205, 239, 1));
} }
SUBCASE("operator!=") SUBCASE("operator!=")
{ {
CHECK(sf::IpAddress() != sf::IpAddress("1.1.1.1")); CHECK(sf::IpAddress(0x12344321) != sf::IpAddress(1234));
CHECK(sf::IpAddress(0x12344321) != sf::IpAddress(""));
CHECK(sf::IpAddress(192, 168, 1, 10) != sf::IpAddress(192, 168, 1, 11)); CHECK(sf::IpAddress(192, 168, 1, 10) != sf::IpAddress(192, 168, 1, 11));
} }
SUBCASE("operator<") SUBCASE("operator<")
{ {
CHECK(sf::IpAddress(1) < sf::IpAddress(2)); CHECK(sf::IpAddress(1) < sf::IpAddress(2));
CHECK(sf::IpAddress() < sf::IpAddress(0, 0, 0, 0));
CHECK(sf::IpAddress(0, 0, 0, 0) < sf::IpAddress(1, 0, 0, 0)); CHECK(sf::IpAddress(0, 0, 0, 0) < sf::IpAddress(1, 0, 0, 0));
CHECK(sf::IpAddress(1, 0, 0, 0) < sf::IpAddress(0, 1, 0, 0)); CHECK(sf::IpAddress(1, 0, 0, 0) < sf::IpAddress(0, 1, 0, 0));
CHECK(sf::IpAddress(0, 1, 0, 0) < sf::IpAddress(0, 0, 1, 0)); CHECK(sf::IpAddress(0, 1, 0, 0) < sf::IpAddress(0, 0, 1, 0));
@ -160,7 +117,6 @@ TEST_CASE("sf::IpAddress class - [network]")
SUBCASE("operator>") SUBCASE("operator>")
{ {
CHECK(sf::IpAddress(2) > sf::IpAddress(1)); CHECK(sf::IpAddress(2) > sf::IpAddress(1));
CHECK(sf::IpAddress(0, 0, 0, 0) > sf::IpAddress());
CHECK(sf::IpAddress(1, 0, 0, 0) > sf::IpAddress(0, 0, 0, 0)); CHECK(sf::IpAddress(1, 0, 0, 0) > sf::IpAddress(0, 0, 0, 0));
CHECK(sf::IpAddress(0, 1, 0, 0) > sf::IpAddress(1, 0, 0, 0)); CHECK(sf::IpAddress(0, 1, 0, 0) > sf::IpAddress(1, 0, 0, 0));
CHECK(sf::IpAddress(0, 0, 1, 0) > sf::IpAddress(0, 1, 0, 0)); CHECK(sf::IpAddress(0, 0, 1, 0) > sf::IpAddress(0, 1, 0, 0));
@ -171,49 +127,44 @@ TEST_CASE("sf::IpAddress class - [network]")
SUBCASE("operator<=") SUBCASE("operator<=")
{ {
CHECK(sf::IpAddress(1) <= sf::IpAddress(2)); CHECK(sf::IpAddress(1) <= sf::IpAddress(2));
CHECK(sf::IpAddress() <= sf::IpAddress(0, 0, 0, 0));
CHECK(sf::IpAddress(0, 0, 0, 0) <= sf::IpAddress(1, 0, 0, 0)); CHECK(sf::IpAddress(0, 0, 0, 0) <= sf::IpAddress(1, 0, 0, 0));
CHECK(sf::IpAddress(1, 0, 0, 0) <= sf::IpAddress(0, 1, 0, 0)); CHECK(sf::IpAddress(1, 0, 0, 0) <= sf::IpAddress(0, 1, 0, 0));
CHECK(sf::IpAddress(0, 1, 0, 0) <= sf::IpAddress(0, 0, 1, 0)); CHECK(sf::IpAddress(0, 1, 0, 0) <= sf::IpAddress(0, 0, 1, 0));
CHECK(sf::IpAddress(0, 0, 1, 0) <= sf::IpAddress(0, 0, 0, 1)); CHECK(sf::IpAddress(0, 0, 1, 0) <= sf::IpAddress(0, 0, 0, 1));
CHECK(sf::IpAddress(0, 0, 0, 1) <= sf::IpAddress(1, 0, 0, 1)); CHECK(sf::IpAddress(0, 0, 0, 1) <= sf::IpAddress(1, 0, 0, 1));
CHECK(sf::IpAddress() <= sf::IpAddress());
CHECK(sf::IpAddress("") <= sf::IpAddress(""s));
CHECK(sf::IpAddress("8.8.8.8") <= sf::IpAddress(8, 8, 8, 8));
CHECK(sf::IpAddress(0x42, 0x69, 0x96, 0x24) <= sf::IpAddress(0x42699624)); CHECK(sf::IpAddress(0x42, 0x69, 0x96, 0x24) <= sf::IpAddress(0x42699624));
CHECK(sf::IpAddress(0xABCDEF01) <= sf::IpAddress("171.205.239.1")); CHECK(sf::IpAddress(0xABCDEF01) <= sf::IpAddress(171, 205, 239, 1));
} }
SUBCASE("operator>=") SUBCASE("operator>=")
{ {
CHECK(sf::IpAddress(2) >= sf::IpAddress(1)); CHECK(sf::IpAddress(2) >= sf::IpAddress(1));
CHECK(sf::IpAddress(0, 0, 0, 0) >= sf::IpAddress());
CHECK(sf::IpAddress(1, 0, 0, 0) >= sf::IpAddress(0, 0, 0, 0)); CHECK(sf::IpAddress(1, 0, 0, 0) >= sf::IpAddress(0, 0, 0, 0));
CHECK(sf::IpAddress(0, 1, 0, 0) >= sf::IpAddress(1, 0, 0, 0)); CHECK(sf::IpAddress(0, 1, 0, 0) >= sf::IpAddress(1, 0, 0, 0));
CHECK(sf::IpAddress(0, 0, 1, 0) >= sf::IpAddress(0, 1, 0, 0)); CHECK(sf::IpAddress(0, 0, 1, 0) >= sf::IpAddress(0, 1, 0, 0));
CHECK(sf::IpAddress(0, 0, 0, 1) >= sf::IpAddress(0, 0, 1, 0)); CHECK(sf::IpAddress(0, 0, 0, 1) >= sf::IpAddress(0, 0, 1, 0));
CHECK(sf::IpAddress(1, 0, 0, 1) >= sf::IpAddress(0, 0, 0, 1)); CHECK(sf::IpAddress(1, 0, 0, 1) >= sf::IpAddress(0, 0, 0, 1));
CHECK(sf::IpAddress() >= sf::IpAddress());
CHECK(sf::IpAddress("") >= sf::IpAddress(""s));
CHECK(sf::IpAddress("8.8.8.8") >= sf::IpAddress(8, 8, 8, 8));
CHECK(sf::IpAddress(0x42, 0x69, 0x96, 0x24) >= sf::IpAddress(0x42699624)); CHECK(sf::IpAddress(0x42, 0x69, 0x96, 0x24) >= sf::IpAddress(0x42699624));
CHECK(sf::IpAddress(0xABCDEF01) >= sf::IpAddress("171.205.239.1")); CHECK(sf::IpAddress(0xABCDEF01) >= sf::IpAddress(171, 205, 239, 1));
} }
SUBCASE("operator>>") SUBCASE("operator>>")
{ {
sf::IpAddress ipAddress; std::optional<sf::IpAddress> ipAddress;
std::istringstream("4.4.4.4") >> ipAddress; std::istringstream("4.4.4.4") >> ipAddress;
CHECK(ipAddress.toString() == "4.4.4.4"s); REQUIRE(ipAddress.has_value());
CHECK(ipAddress.toInteger() == 0x04040404); CHECK(ipAddress->toString() == "4.4.4.4"s);
CHECK(ipAddress != sf::IpAddress::None); CHECK(ipAddress->toInteger() == 0x04040404);
std::istringstream("92.100.0.72") >> ipAddress; std::istringstream("92.100.0.72") >> ipAddress;
CHECK(ipAddress.toString() == "92.100.0.72"s); REQUIRE(ipAddress.has_value());
CHECK(ipAddress.toInteger() == 0x5C640048); CHECK(ipAddress->toString() == "92.100.0.72"s);
CHECK(ipAddress != sf::IpAddress::None); CHECK(ipAddress->toInteger() == 0x5C640048);
std::istringstream("") >> ipAddress;
CHECK(!ipAddress.has_value());
} }
SUBCASE("operator<<") SUBCASE("operator<<")