Loading...
Searching...
No Matches

Multiplexer that allows to read from multiple sockets. More...

#include <D:/private/SFML/SFML/include/SFML/Network/SocketSelector.hpp>

Public Types

enum  : ReadinessType { Receive = 1 << 0 , Send = 1 << 1 }
 Type of readiness to check for. More...
using ReadinessType = std::uint32_t
 Bitwise combination of readiness types.

Public Member Functions

 SocketSelector ()
 Default constructor.
 ~SocketSelector ()
 Destructor.
 SocketSelector (const SocketSelector &copy)
 Copy constructor.
SocketSelectoroperator= (const SocketSelector &right)
 Overload of assignment operator.
 SocketSelector (SocketSelector &&) noexcept
 Move constructor.
SocketSelectoroperator= (SocketSelector &&) noexcept
 Move assignment.
bool add (const Socket &socket, ReadinessType readinessType=Receive, std::function< void(ReadinessType readinessType)> readyCallback={})
 Add a new socket to the selector.
bool remove (const Socket &socket)
 Remove a socket from the selector.
void clear ()
 Remove all the sockets stored in the selector.
bool wait (Time timeout=Time::Zero)
 Wait until one or more sockets are ready to receive or send.
bool isReady (const Socket &socket, ReadinessType readinessType=Receive) const
 Test a socket to know if it is ready to receive or send data.
void dispatchReadyCallbacks ()
 Dispatch callbacks of ready sockets.

Detailed Description

Multiplexer that allows to read from multiple sockets.

Socket selectors provide a way to wait until data can be received or sent 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. When sending large amounts of data, the socket send buffer might fill up and you will have to wait for the data to actually be sent over the network connection before attempting to send more data.

All types of sockets can be used in a selector:

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:

  • populate the selector with all the sockets that you want to observe
  • make it wait until there is data available or data can be sent on any of the sockets
  • test each socket to find out which ones are ready for receiving or sending

If scalability is a concern, using callbacks is also possible:

  • add all the sockets that you want to observe to the selector with their own ready callbacks
  • make the selector wait until there is data available or data can be sent on any of the sockets
  • dispatch the callbacks of the sockets that became ready during the wait

Usage example:

// 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
// 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)
{
...
}
}
}
}
}
}
Multiplexer that allows to read from multiple sockets.
bool isReady(const Socket &socket, ReadinessType readinessType=Receive) const
Test a socket to know if it is ready to receive or send data.
bool wait(Time timeout=Time::Zero)
Wait until one or more sockets are ready to receive or send.
bool add(const Socket &socket, ReadinessType readinessType=Receive, std::function< void(ReadinessType readinessType)> readyCallback={})
Add a new socket to the selector.
@ Done
The socket has sent / received the data.
Definition Socket.hpp:51
Socket that listens to new TCP connections.
Status listen(unsigned short port, IpAddress address=IpAddress::Any)
Start listening for incoming connection attempts.
Status accept(TcpSocket &socket)
Accept a new connection.
Specialized socket using the TCP protocol.
Definition TcpSocket.hpp:58
Status receive(void *data, std::size_t size, std::size_t &received)
Receive raw data from the remote peer.

Usage example with callbacks:

// 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
// We use a std::list here because modifications
// don't invalidate references to existing elements
std::list<sf::TcpSocket> clients;
// Create a selector
// Listeners can only become ready to receive so we don't
// have to check the readiness type in their callback
const auto listenerCallback = [&](sf::SocketSelector::ReadinessType)
{
// The listener is ready: there is a pending connection
sf::TcpSocket newSocket;
if (listener.accept(newSocket) == sf::Socket::Status::Done)
{
// Add the new client to the clients list
auto& client = clients.emplace_back(std::move(newSocket));
const auto clientCallback = [&client](sf::SocketSelector::ReadinessType readinessType)
{
if (readinessType & sf::SocketSelector::Receive)
{
// The client has sent some data, we can receive it
sf::Packet packet;
if (client.receive(packet) == sf::Socket::Status::Done)
{
...
}
}
};
// Add the new client to the selector with an attached
// callback that will be called when they send something
selector.add(client, sf::SocketSelector::Receive, clientCallback);
}
else
{
// Handle error...
}
};
// Add the listener to the selector with an attached callback
selector.add(listener, sf::SocketSelector::Receive, listenerCallback);
// Endless loop that waits for new connections and receives client data
while (running)
{
// Make the selector wait for sockets to become ready and dispatch their callbacks
if (selector.wait())
}
Utility class to build blocks of data to transfer over the network.
Definition Packet.hpp:49
std::uint32_t ReadinessType
Bitwise combination of readiness types.
@ Receive
Check if sockets are ready to be received from.
void dispatchReadyCallbacks()
Dispatch callbacks of ready sockets.
See also
sf::Socket

Definition at line 48 of file SocketSelector.hpp.

Member Typedef Documentation

◆ ReadinessType

using sf::SocketSelector::ReadinessType = std::uint32_t

Bitwise combination of readiness types.

Definition at line 51 of file SocketSelector.hpp.

Member Enumeration Documentation

◆ anonymous enum

anonymous enum : ReadinessType

Type of readiness to check for.

Enumerator
Receive 

Check if sockets are ready to be received from.

Send 

Check if sockets are ready to be sent to.

Definition at line 57 of file SocketSelector.hpp.

Constructor & Destructor Documentation

◆ SocketSelector() [1/3]

sf::SocketSelector::SocketSelector ( )

Default constructor.

◆ ~SocketSelector()

sf::SocketSelector::~SocketSelector ( )

Destructor.

◆ SocketSelector() [2/3]

sf::SocketSelector::SocketSelector ( const SocketSelector & copy)

Copy constructor.

Parameters
copyInstance to copy

◆ SocketSelector() [3/3]

sf::SocketSelector::SocketSelector ( SocketSelector && )
noexcept

Move constructor.

Member Function Documentation

◆ add()

bool sf::SocketSelector::add ( const Socket & socket,
ReadinessType readinessType = Receive,
std::function< void(ReadinessType readinessType)> readyCallback = {} )

Add a new socket to the selector.

The type of readiness to wait for can be specified. Specifying SocketSelector::Receive will wait for the socket to become ready to receive data from, specifying SocketSelector::Send will wait for the socket to become ready to send data on. Specifying SocketSelector::Receive | SocketSelector::Send will wait for the socket to become either ready to send or receive data on.

Adding a socket after it has already been added will just overwrite the existing readiness type with the new value.

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.

When adding a socket to the selector you can also attach a callback along with it. The callback is called by dispatchReadyCallbacks when a socket is determined to be ready after a call to wait.

Using attached callbacks instead of having to individually call isReady on every socket after every call to wait allows for scaling up to a large number of sockets. This is because the overhead of checking for socket readiness using isReady grows proportionally to the total number of sockets. When using callbacks calling isReady on every socket is no longer necessary.

Because a socket can be ready for receiving, sending or both, the type of readiness is passed to the attached callback as a bitwise combination of SocketSelector::Receive and/or SocketSelector::Send when it is called by dispatchReadyCallbacks. Some systems don't support combined read and write notifications. On these systems, if a socket is ready to be both received from and sent to the callback will be called twice, once with SocketSelector::Receive and once with SocketSelector::Send.

To remove the attached callback of a socket, call add again with an empty function.

By default, no readiness callback is attached when adding a socket.

Parameters
socketReference to the socket to add
readinessTypeType of readiness to wait for, a bitwise combination of SocketSelector::Receive and/or SocketSelector::Send
readyCallbackReady callback to attach to the socket, pass an empty function to remove the ready callback
Returns
true if the socket was added successfully, false otherwise
See also
remove, clear

◆ clear()

void sf::SocketSelector::clear ( )

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 also
add, remove

◆ dispatchReadyCallbacks()

void sf::SocketSelector::dispatchReadyCallbacks ( )

Dispatch callbacks of ready sockets.

After calling wait returns true, at least one socket is ready to receive or send data. Calling dispatchReadyCallbacks will call the attached ready callback for every socket that is ready to either receive or send data. Sockets that don't have a callback attached can still be individually checked using isReady.

The readiness state of each socket is maintained until the next call to wait. Calling dispatchReadyCallbacks multiple times after a single call to wait will run the exact same callbacks with the exact same passed arguments.

See also
wait

◆ isReady()

bool sf::SocketSelector::isReady ( const Socket & socket,
ReadinessType readinessType = Receive ) const
nodiscard

Test a socket to know if it is ready to receive or send data.

This function must be used after a call to wait, to know which sockets are ready to receive or send data. If a socket is ready, a call to receive or send will never block because we know that there is data available to read or we can write. Note that if this function returns true for a TcpListener, this means that it is ready to accept a new connection.

Parameters
socketSocket to test
readinessTypeType of readiness to check for, a bitwise combination of SocketSelector::Receive and/or SocketSelector::Send
Returns
true if the socket is ready to read, false otherwise
See also
wait

◆ operator=() [1/2]

SocketSelector & sf::SocketSelector::operator= ( const SocketSelector & right)

Overload of assignment operator.

Parameters
rightInstance to assign
Returns
Reference to self

◆ operator=() [2/2]

SocketSelector & sf::SocketSelector::operator= ( SocketSelector && )
noexcept

Move assignment.

◆ remove()

bool sf::SocketSelector::remove ( const Socket & socket)

Remove a socket from the selector.

This function doesn't destroy the socket, it simply removes the reference that the selector has to it.

Parameters
socketReference to the socket to remove
Returns
true if the socket was removed successfully, false otherwise
See also
add, clear

◆ wait()

bool sf::SocketSelector::wait ( Time timeout = Time::Zero)
nodiscard

Wait until one or more sockets are ready to receive or send.

This function returns as soon as at least one socket has some data available to be received or data can be sent, depending on how the socket was added to this selector. 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.

Parameters
timeoutMaximum time to wait, (use Time::Zero for infinity)
Returns
true if there are sockets ready, false otherwise
See also
isReady, dispatchReadyCallbacks

The documentation for this class was generated from the following file: