我正在研究服务器 - 客户端结构,我需要在服务器和客户端之间发送一个字符串,这样做我创建一个包含int(数据包类型)和字符串的结构,然后我将序列化和通过tcp连接发送到客户端,然后反序列化它。
我的问题是我在客户端获取了错误的字符串,服务器端的字符串是:“PO-1-25-25 \ nPO-2-50-50 \ n”,但我得到的字符串在客户端是“\ x18 = $”。
我发送的结构(在客户端也是相同的结构)
struct Packet {
unsigned int packet_type;
std::string message;
void serialize(char * data) {
memcpy(data, this, sizeof(Packet));
}
void deserialize(char * data) {
memcpy(this, data, sizeof(Packet));
}
};
我发送结构的代码:
void sendGameStatePacket(unsigned int receiver){
const unsigned int packet_size = sizeof(Packet);
char packet_data[packet_size];
Packet packet;
packet.packet_type = GAME_STATE;
packet.message = message;
packet.serialize(packet_data);
network->sendToOne(packet_data, packet_size, receiver);
}
void sendToOne(char * packets, int totalSize, unsigned int socketID){
SOCKET currentSocket = sessions[socketID];
int iSendResult;
iSendResult = NetworkServices::sendMessage(currentSocket, packets,totalSize);
}
int sendMessage(SOCKET curSocket, char * message, int messageSize)
{
return send(curSocket, message, messageSize, 0);
}
接收结构的客户端代码:
char network_data[MAX_PACKET_SIZE]; //MAX_PACKET_SIZE = 1000000
void AClientGame::update()
{
Packet packet;
int data_length = network->receivePackets(network_data);
unsigned int i = 0;
while (i < (unsigned int)data_length)
{
packet.deserialize(&(network_data[i]));
i += sizeof(Packet);
FString message;
message = packet.message.c_str();
UE_LOG(LogTemp, Warning, TEXT("Test: %s"), *message);
}
}
int receivePackets(char * recvbuf)
{
iResult = NetworkServices::receiveMessage(ConnectSocket, recvbuf, MAX_PACKET_SIZE);
}
int receiveMessage(SOCKET curSocket, char * buffer, int bufSize)
{
return recv(curSocket, buffer, bufSize, 0);
}
我已经检查过客户端的package.message是“\ x18 = $”字符串,所以问题不在于从字符串到FString的转换。
我的套接字配置如下:
服务器:
network::ServerNetwork::ServerNetwork(void)
{
// create WSADATA object
WSADATA wsaData;
// our sockets for the server
ListenSocket = INVALID_SOCKET;
ClientSocket = INVALID_SOCKET;
// address info for the server to listen to
struct addrinfo *result = NULL;
struct addrinfo hints;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
exit(1);
}
// set address information
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; // TCP connection!!!
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
exit(1);
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
exit(1);
}
// Set the mode of the socket to be nonblocking
u_long iMode = 1;
iResult = ioctlsocket(ListenSocket, FIONBIO, &iMode);
if (iResult == SOCKET_ERROR) {
printf("ioctlsocket failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
exit(1);
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
exit(1);
}
// no longer need address information
freeaddrinfo(result);
// start listening for new clients attempting to connect
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
exit(1);
}
}
客户端:
ClientNetwork::ClientNetwork()
{
// create WSADATA object
WSADATA wsaData;
// socket
ConnectSocket = INVALID_SOCKET;
// holds address info for socket to connect to
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
exit(1);
}
// set address info
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP; //TCP connection!!!
//resolve server address and port
iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
exit(1);
}
// Attempt to connect to an address until one succeeds
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
WSACleanup();
exit(1);
}
// Connect to server.
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
printf("The server is down... did not connect");
}
}
// no longer need address info for server
freeaddrinfo(result);
// if connection failed
if (ConnectSocket == INVALID_SOCKET)
{
printf("Unable to connect to server!\n");
WSACleanup();
exit(1);
}
// Set the mode of the socket to be nonblocking
u_long iMode = 1;
iResult = ioctlsocket(ConnectSocket, FIONBIO, &iMode);
if (iResult == SOCKET_ERROR)
{
printf("ioctlsocket failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
exit(1);
}
//disable nagle
char value = 1;
setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
}
如果有人能解释为什么它不起作用以及如何解决它将是一个很大的帮助
答案 0 :(得分:1)
首先,表达式sizeof(packet)
不会给出结构的大小,包括字符串,因为字符串很可能只是一个指针。
由于字符串只是一个指针,你在序列化函数中复制的数据不是字符串而是指针,你不能通过网络发送指针,因为它们对于单个系统上的单个进程是唯一的
您必须获取字符串的实际大小,然后分配该内存量(以及您需要发送的任何其他内容)并使用该大小。当然,由于字符串可以是可变大小,因此您需要在消息头中发送消息的实际大小。
答案 1 :(得分:1)
struct Packet {
unsigned int packet_type;
std::string message;
void serialize(char * data) {
memcpy(data, this, sizeof(Packet));
}
void deserialize(char * data) {
memcpy(this, data, sizeof(Packet));
}
};
这有很多错误,很难知道从哪里开始。首先,deserialize
的调用者如何知道传递多少字节?其次,实际序列化message
的代码在哪里?用于计算结构大小及其中的数据的代码在哪里?
当你&#34;序列化&#34;某些东西,你必须将它安排成特定的字节格式。这种格式是什么?根本没有代码可以将消息转换为任何特定格式。
这是希望通过魔法工作的代码。
如果您要使用TCP,在编写一行代码之前,请写出您将用于在字节级别交换数据的协议的规范。涵盖如何分隔消息,哪一方将在何时传输,等等。您可以查看HTTP和SMTP之类的现有规范,以了解规范应该是什么样的。
序列化代码应该生成规范要求的精确字节格式。反序列化代码应遵循规范的分隔消息规则。
答案 2 :(得分:0)
你可以在&#34;字符串&#34;前面这样做。你添加的字节,例如两个其他字节,它们代表&#34;字符串&#34;长度(多少个字符)。所以长度为first_byte*256+second_byte
当您在单个数据包中从客户端向服务器(或其他方向)发送多个字符串时,这将非常有用。然后你只需计算偏移量。