通过 TCP (c++) 在结构中发送字符串
Sending string in a struct over TCP (c++)
我正在研究服务器-客户端结构,我需要在服务器和客户端之间发送字符串,为此,我创建了一个包含 int(数据包类型)和字符串的结构,然后我序列化并通过 tcp 连接发送到客户端,然后反序列化它。
我的问题是我在客户端得到错误的字符串,服务器端的字符串是:"PO-1-25-25PO-2-50-50",但我在客户端得到的字符串是"\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: %dn", 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: %dn", 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: %ldn", 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: %dn", 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: %dn", 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: %dn", 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: %dn", 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: %dn", 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: %ldn", 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: %dn", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
exit(1);
}
//disable nagle
char value = 1;
setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
}
如果有人可以解释为什么它不起作用以及如何修复它将是一个很大的帮助
首先,表达式 sizeof(packet)
不会为您提供包括字符串在内的结构的大小,因为字符串很可能只是一个指针。
因此您在序列化函数中复制的数据不是字符串而是指针,并且您不能通过网络发送指针,因为它们对于单个系统上的单个进程是唯一的。
您必须获取字符串的实际大小,然后分配该内存量(加上您需要发送的任何其他内容)并使用该大小。当然,由于字符串的大小可以是可变的,因此您还需要在消息标头中发送消息的实际大小。
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
的代码在哪里?计算包含数据的结构大小的代码在哪里?
当你"序列化"某些东西时,你必须把它排列成特定的字节格式。这是什么格式?根本没有代码可以将消息转换为任何特定格式。
这是期望事情通过魔术工作的代码。
如果你打算使用TCP,在你编写哪怕一行代码之前,写出一个规范,你应该使用在字节级别交换数据。涵盖如何分隔消息、何时传输哪一端等。您可以查看HTTP和SMTP等一些现有规范,以了解规范应该是什么样子。
序列化代码应生成规范要求的精确字节格式。反序列化代码应遵循规范的分隔消息规则。
这样做,以便在"字符串"字节前面添加例如另外两个字节,它们将代表"字符串"长度(多少个字符)。 所以长度将是first_byte*256+second_byte
当您在单个数据包中将多个字符串从客户端发送到服务器(或其他方向)时,这将非常有用。 然后,您只需计算偏移量。
- C++结构到字符串
- 从字符串中获取结构的大小
- 用于存储由空格分隔的字符串的 C++/C 数据结构
- 如何解析表示树状数据结构的字符串
- 如何将字符串与结构(也包括字符串)进行比较?
- 如果结构中的字符串比使用的 p/调用签名长或短,会发生什么情况?
- 在向量 C++14(无限制联合)的结构内的联合中创建和存储字符串
- C++ - 使用用户输入的字符串数据检查结构字符串数据(无限执行 while 循环)
- 将字符串存储在 constexpr 结构中
- 为什么我的结构在包含字符串时崩溃?
- 如何将字符串字符与结构数组进行比较?
- 无法使用 strcpy 将字符串复制到静态结构成员
- c++ 将函数参数(带有模板变量的结构)转换为函数体内的字符串
- 在 c++ 中使用字符串变量选择静态类结构,而不带对象
- C++使用旧编译器GCC 4.4.0在结构列表中查找字符串
- 具有字符串成员的结构的动态数组
- 如何将文本文件保存到C 中的字符串结构
- C++ 斯特劳斯特鲁普的"std_lib_facilities.h"字符串结构 – 警告:无符号表达式的比较
- C++:字符串结构相等
- 返回字符串结构是否安全