I programmed a pong multiplayer game using SFML, where i used sf::UdpSockets for transferring data between two PCs. It worked great. Next i wanted to check out how the result would look like if i used the asio network library for networking instead of the SFML Network-classes.
Here my first big issue was, that asio does not provide a class similar to sf::Packet that helps with serializing the data.
With SFML its so beautifully simple, for example if you want to store an enum and some data that belongs to the enum, you could just do:
enum class CommandsToServer{
Connect, // empty
Disconnect, // empty
Data // Vector2f
};
...
sf::Packet packet;
packet << static_cast<uint8_t>(CommandsToServer::Data);
packet << x << y;
and send the packet to the server, where you unpack the content the same way you put it in.
uint8_t command;
uint8_t x, y;
// ... receive packet from socket ...
receivedPacket >> command >> x >> y;
CommandToServer commandToServer = static_cast<CommandToServer>(command);
Since i did not find a straightforward way to pack different types into an array, i just used sf::Packet again with the asio::ip::udp::socket.
This approach looks like this: m_packet << static_cast<uint8_t>(5);
m_packet << static_cast<uint8_t>(6);
m_socket.send_to(asio::buffer(m_packet.getData(), m_packet.getDataSize()), m_serverEndpoint);
So i just try to transfer two values over the network: 5 and 6.
On the server-side i use
std::array<char, 256> m_receiveBuffer;
...
// in some function
m_socket.async_receive_from(
asio::buffer(m_receiveBuffer), m_remoteEndpoint,
std::bind(&Server::handle_receive, this,
std::placeholders::_1,
std::placeholders::_2));
...
void Server::handle_receive(const asio::error_code &error, std::size_t l_receivedBytes) {
sf::Packet packet;
std::cout << "received bytes: " << int(l_receivedBytes) << "\n"; // Output: received bytes 2
packet.append(m_receiveBuffer.data(), l_receivedBytes);
int num1; int num2;
packet >> num1 >> num2;
std::cout << num1 << ", " << num2 << ".\n"; // Output: 0, 0
receiveData();
Console prints 0, 0. But if i start the debugger and set a breakpoint at the last std::cout-statement, i can see that
packet has the following members:
m_data
[ 0 ] = {char}5 '\005'
[ 1 ] = {char}6 '\006’
m_readPos = 0
m_sendPos = 0
m_isValid = false
Its strange because the values 5 and 6 have apparently been transferred successfully, since they are stored in the std::vector<char> container of sf::Packet. However, they don't get transferred via the >> operator into the variables num1 and num2. I don't know the reason why i cannot transfer the data out of the sf::Packet into the variables on the server-side.
i found out that the checkSize() method from
// SFML Packet.cpp
Packet& Packet::operator >>(Int32& data)
{
if (checkSize(sizeof(data)))
{
std::memcpy(&data, &m_data[m_readPos], sizeof(data));
data = ntohl(data);
m_readPos += sizeof(data);
}
return *this;
}
returns false, thus the data does not get copied into the Int32& data parameter. But i don't know why the check fails.
Thank you for the replies! Laurents answer got me on the right track.
I changed the function to
void Server::handle_receive(const asio::error_code &error, std::size_t l_receivedBytes) {
sf::Packet packet;
std::cout << "received bytes: " << int(l_receivedBytes) << "\n";
packet.append(m_receiveBuffer.data(), l_receivedBytes);
uint8_t num1; uint8_t num2;
packet >> num1 >> num2;
std::cout << int(num1) << ", " << int(num2) << ".\n"; // Output: 5, 6
receiveData();
}
and now the console prints the right values. One of the reasons i got it wrong at first was that i initially even tried using
uint8_t num1
instead of
int num1
but printing uint_t to the console resulted in strange symbols displayed in the console instead of the numerical values, therefore i wrongly assumed that i produced an error using uint8_t and switched to int, which at least printed some numerical values to the console. My bad.
However, every time i use some other library (like asio) it reminds me about how well designed SFML is and how effectively it is in hiding the confusing low-level stuff, while still allowing to mix it with low-level libs, if needed.