[英]Sending string in a struct over TCP (c++)
我正在處理服務器-客戶端結構,在該結構中,我需要在服務器和客戶端之間發送字符串,為此,我創建了一個包含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));
}
如果有人可以解釋為什么它不起作用以及如何解決它將會有很大的幫助
首先,表達式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
當您在單個數據包中從客戶端向服務器(或其他方向)發送多個字符串時,這將非常有用。 然后,您只需計算偏移量即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.