I am curently trying to add a multiplayer system to my game engine. To do so i decided to use the TCP approch. Since i am already using the SDL2 library i decided to use the SDL2-net library for networking.
I wish to send data to the server and vice versa. Sending small amounts of data isn't a problem however when i want to send a big chunk of data, a piece of terrain for example, the client will receiver will might get some other data mixed in between the terrain data which is of course not wanted. I am no expert in networking so there might be some basic concepts that i do not know of, and i would be more than happy to know if there is a solution to this "problem".
Thanks for your help!
In the following code a client sends a large array of numbers(in Client constructor) to a server and also a message when the client object gets destroyed(in Client destructor) but these two messages get mixed up.
Client.h
#ifndef CLIENT_H
#define CLIENT_H
#include <SDL2/SDL_net.h>
namespace gt
{
namespace ns
{
namespace networking
{
class Client
{
private:
TCPsocket socket;
SDLNet_SocketSet socketSet;
void sendData(const char*) const;
public:
Client(const char*, int);
~Client();
void checkIncomingData();
};
}
}
}
#endif
Client.cpp
#include "Client.h"
#include <iostream>
#include <cstring>
namespace gt
{
namespace ns
{
namespace networking
{
Client::Client(const char* address, int port)
{
SDLNet_Init();
IPaddress ip;
if(SDLNet_ResolveHost(&ip, address, port) == -1)
{
std::cout << "Client connection error!" << std::endl;
}
socket = SDLNet_TCP_Open(&ip);
if(socket == NULL)
{
std::cout << "Client connection error! (wrong ip address?)" << std::endl;
}
socketSet = SDLNet_AllocSocketSet(1);
SDLNet_TCP_AddSocket(socketSet, socket);
sendData("<for the sake of simlicity i removed the numbers that were here>\n");
}
Client::~Client()
{
sendData("Client disconecting!\n");
SDLNet_TCP_Close(socket);
SDLNet_FreeSocketSet(socketSet);
SDLNet_Quit();
}
void Client::sendData(const char* data) const
{
int size = strlen(data);
int sentsize = 0;
while(sentsize < size)
{
sentsize+=SDLNet_TCP_Send(socket, data + sentsize, size - sentsize);
}
}
void Client::checkIncomingData()
{
while(SDLNet_CheckSockets(socketSet,0) > 0)
{
if(SDLNet_SocketReady(socket))
{
char data[1400];
SDLNet_TCP_Recv(socket, data, 1400);
std::cout << "data received: " << data << std::endl;
}
}
}
}
}
}
Server.h
#ifndef SERVER_H
#define SERVER_H
#include <SDL2/SDL_net.h>
#include "ClientData.h"
#include "../utils/ArrayList.h"
namespace gt
{
namespace ns
{
namespace networking
{
class Server
{
private:
int maxClients;
IPaddress ip;
TCPsocket socket;
ArrayList<ClientData> clients;
SDLNet_SocketSet clientsSet;
int currentId;
void removeClient(int);
void checkNewConnection();
void checkIncomingData();
void checkTimeouts(float);
bool readData(const TCPsocket&);
public:
Server(int, int);
~Server();
void update(float);
};
}
}
}
#endif
Server.cpp
#include "Server.h"
#include <iostream>
#include <cstring>
namespace gt
{
namespace ns
{
namespace networking
{
Server::Server(int maxClients, int port)
{
this->maxClients = maxClients;
clientsSet = SDLNet_AllocSocketSet(maxClients);
SDLNet_ResolveHost(&ip, NULL, port);
socket = SDLNet_TCP_Open(&ip);
currentId = 1;
}
Server::~Server()
{
SDLNet_FreeSocketSet(clientsSet);
SDLNet_TCP_Close(socket);
}
void Server::removeClient(int index)
{
SDLNet_TCP_DelSocket(clientsSet, clients[index].getSocket());
clients.remove(index);
}
void Server::checkNewConnection()
{
TCPsocket clientSocket = SDLNet_TCP_Accept(socket);
if(clientSocket)
{
if(clients.size() < maxClients)
{
SDLNet_TCP_AddSocket(clientsSet, clientSocket);
ClientData data(currentId, clientSocket);
clients.add(data);
std::cout << "new connection id: " << currentId << std::endl;
currentId++;
}
else
{
//no more space!
}
}
}
void Server::checkIncomingData()
{
while(SDLNet_CheckSockets(clientsSet, 0) > 0)
{
for(int i=0;i<clients.size();i++)
{
if(SDLNet_SocketReady(clients[i].getSocket()))
{
if(readData(clients[i].getSocket()))
{
clients[i].timer.reset();
}
else
{
removeClient(i);
}
}
}
}
}
void Server::checkTimeouts(float timePassed)
{
for(int i=0;i<clients.size();i++)
{
clients[i].timer.update(timePassed);
if(clients[i].timer.getTime() > 5.0)
{
removeClient(i);
}
}
}
void Server::update(float timePassed)
{
checkNewConnection();
checkIncomingData();
checkTimeouts(timePassed);
}
bool Server::readData(const TCPsocket& clientSocket)
{
char data[1400];
int size = SDLNet_TCP_Recv(clientSocket, data, 1400);
if(size <= 0)
{
std::cout << "RECEIVING DATA FAILED: " << size << std::endl;
return false;
}
std::cout << data << std::endl;
while(data[strlen(data) - 1] != '\n')
{
size = SDLNet_TCP_Recv(clientSocket, data, 1400);
if(size <= 0)
{
std::cout << "RECEIVING DATA FAILED: " << size << std::endl;
return false;
}
std::cout << data << std::endl;
}
return true;
}
}
}
}
what i get in the server terminal when i create the Client object and delete right after(the array in code is bigger i just chose the interesting part of the output):
0.1519227 -0.002553641 -0.3744464 0.1526676 0 -0.06999236 0.1529192 0.002553701 -0.06999236 0.1526677 0.005009293 -0.06999236 0.1519228 0.007272362 -0.06999236 0.1507131 0.009255945 -0.06999236 0.149085
2 0.01088386 -0.06999236 0.1471016 0.01209348 -0.06999236 0.1448385 0.01283836 -0.06999236 0.142383 0.01308989 -0.06999236 0.1398292 0.01283836 -0.06999236 0.1372755 0.01209348 -0.06999236 0.13482 0.01088386 -0.06999236 0.1325569 0.009255945 -0.06999236 0.1305733 0.007272362 -0.06999236 0.1289454 0.005009293 -0.06999236 0.1277357 0.002553701 -0.06999236 0.1269909 0 -0.06999236 0.1267393 -0.002553701 -0.06999236 0.1269909 -0.005009293 -0.06999236 0.1277357 -0.007272362 -0.06999236 0.1289454 -0.009255945 -0.06999236 0.1305733 -0.01088386 -0.06999236 0.1325569 -0.01209348 -0.06999236 0.13482 -0.01283836 -0.06999236 0.1372755 -0.01308989 -0.06999236 0.1398293 -0.01283836 -0.06999236 0.142383 -0.01209348 -0.06999236 0.1448386 -0.01088386 -0.06999236 0.1471017
.1269907 -0.005009293 -0.3744465 0.1277357 -0.007272362 -0.3744464 0.1289453 -0.009255945 -0.3744465 0.1305732 -0.01088386 -0.3744464 0.1325568 -0.01209348 -0.3744465 0.1348199 -0.01283836 -0.3744465 0.1372755 -0.01308989 -0.3744465 0.1398292 -0.01283836 -0.3744464 0.1423829 -0.01209348 -0.3744464 0.1448385 -0.01088386 -0.3744465 0.1471015 -0.009255945 -0.3744464 0.1490852 -0.007272303 -0.3744464 0.150713 -0.005009233 -0.3744464 0.1519227 -0.002553641 -0.3744464 0.1526676 0 -0.06999236 0.1529192 0.002553701 -0.06999236 0.1526677 0.005009293 -0.06999236 0.1519228 0.007272362 -0.06999236 0.1507131 0.009255945 -0.06999236 0.149085
Client disconecting! <- this message is received in the number array!
236 0.1471016 0.01209348 -0.06999236 0.1448385 0.01283836 -0.06999236 0.142383 0.01308989 -0.06999236 0.1398292 0.01283836 -0.06999236 0.1372755 0.01209348 -0.06999236 0.13482 0.01088386 -0.06999236 0.1325569 0.009255945 -0.06999236 0.1305733 0.007272362 -0.06999236 0.1289454 0.005009293 -0.06999236 0.1277357 0.002553701 -0.06999236 0.1269909 0 -0.06999236 0.1267393 -0.002553701 -0.06999236 0.1269909 -0.005009293 -0.06999236 0.1277357 -0.007272362 -0.06999236 0.1289454 -0.009255945 -0.06999236 0.1305733 -0.01088386 -0.06999236 0.1325569 -0.01209348 -0.06999236 0.13482 -0.01283836 -0.06999236 0.1372755 -0.01308989 -0.06999236 0.1398293 -0.01283836 -0.06999236 0.142383 -0.01209348 -0.06999236 0.1448386 -0.01088386 -0.06999236 0.1471017
.1269907 -0.005009293 -0.3744465 0.1277357 -0.007272362 -0.3744464 0.1289453 -0.009255945 -0.3744465 0.1305732 -0.01088386 -0.3744464 0.1325568 -0.01209348 -0.3744465 0.1348199 -0.01283836 -0.3744465 0.1372755 -0.01308989 -0.3744465 0.1398292 -0.01283836 -0.3744464 0.1423829 -0.01209348 -0.3744464 0.1448385 -0.01088386 -0.3744465 0.1471015 -0.009255945 -0.3744464 0.1490852 -0.007272303 -0.3744464 0.150713 -0.005009233 -0.3744464 0.1519227 -0.002553641 -0.3744464 0.1526676 0 -0.06999236 0.1529192 0.002553701 -0.06999236 0.1526677 0.005009293 -0.06999236 0.1519228 0.007272362 -0.06999236 0.1507131 0.009255945 -0.06999236 0.149085
The problem was comming from the read function. I thought that if i gave it a NULL char array it would fill it up with the incoming data and then stop. However it fills in the char array using the incoming data and then fills up the remaining space of the char array with the prévious data received. (if the incoming data was shorter then the previous). As pointed out user207421 using the size of received data is needed. The fix was actualy simple, the read function returns the size of the received data and 0 or less if there was a connection error.
The fix:
(Although this code will print the data correcly in order packet seperation might still be needed for data processing)