【发布时间】:2019-11-27 22:46:00
【问题描述】:
我目前正在尝试将多人游戏系统添加到我的游戏引擎中。为此,我决定使用 TCP 方法。由于我已经在使用 SDL2 库,因此我决定使用 SDL2-net 库进行网络连接。
我希望向服务器发送数据,反之亦然。发送少量数据不是问题,但是当我想发送大量数据时,例如一块地形,客户端将接收器可能会在地形数据之间混合一些其他数据,这当然不是通缉。我不是网络专家,所以可能有一些我不知道的基本概念,如果有解决这个“问题”的方法,我会非常高兴。
感谢您的帮助!
在以下代码中,客户端向服务器发送大量数字(在客户端构造函数中),并在客户端对象被销毁时(在客户端析构函数中)发送一条消息,但这两条消息混淆了。
客户端.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
客户端.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;
}
}
}
}
}
}
服务器.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
服务器.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;
}
}
}
}
当我创建 Client 对象并在之后立即删除时,我在服务器终端中得到了什么(代码中的数组更大,我只是选择了输出中有趣的部分):
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
【问题讨论】:
-
应用层为什么/如何混合消息? TCP/IP 只是传输应用程序的消息。您需要了解为什么您的应用程序在消息(地形数据)当前正在进行时尝试发送另一条消息。
-
嗯,我不知道,但是当我调用发送所有数据的 Client.sendData() 方法时发送数据,我使用这个函数两次,所以我希望它发送数字数组,然后是“断开连接”消息。但是我认为数字数组很大,我认为它是分几块发送的。 Server.readData() 方法只是打印接收到的数据。发送地形数据后是否需要等待服务器响应?
-
TCP 不发送消息,它发送分段以适应 MSS 的数据流,并在另一端将分段重新组合成一个流。它将简单地将所有消息混合在一起,并且不知道任何消息边界,因此它可能会在任何给定时间将部分消息传递到目标应用程序,但所有数据最终都会按顺序发送到应用程序。有为消息设计的传输协议(UDP、SCTP 等),但不是 TCP。
-
您忽略了读取函数/方法返回的计数。你不能假设每次读取都会填满缓冲区,或者没有到达流的末尾,或者没有失败。
标签: c++ networking tcp sdl-net