C++ TCP套接字发送结构体求助:服务端接收与读取疑问
解决TCP套接字发送结构体的问题
嘿,作为socket编程新手,遇到结构体发送的问题很正常——咱们先拆解你代码里的几个关键问题,然后给出能正常工作的方案~
你的代码里的核心问题
我先帮你梳理下当前代码里导致接收失败或无法正确读取的原因:
- 服务端用错了套接字:你在服务端用
sock(监听套接字)调用recv,但实际上accept返回的cli才是和客户端通信的已连接套接字,监听套接字只能用来接受新连接,不能收发数据。 std::string不能直接序列化发送:std::string是C++对象,内部存储的是指向堆内存的指针,你直接发送结构体的话,只会把指针地址发过去,服务端收到的是无效的内存地址,根本拿不到"4G"这个实际字符串内容。- 循环条件错误:客户端里的
while(count = 0)是赋值操作,不是判断,这会导致循环只执行一次(其实你本来也只需要发一次,完全可以去掉这个循环)。 - 字节序处理不匹配:你用
htons()处理int类型的imsi,但htons()是针对16位短整型的,应该用htonl()处理32位int,服务端接收后要用ntohl()转回来。 - 未检查
recv/send的返回值:这会导致你无法确认数据是否成功发送或接收。
修改后的可运行代码
服务端(Server.cpp)
#include<iostream> #include <cstring> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<string> #include<stdio.h> using namespace std; // 改用固定大小char数组代替std::string,确保结构体可直接序列化 struct UE { char Net[10]; // 预留足够空间存字符串,比如最多9个字符+结束符 int imsi; }; int main() { UE UE2; int sock, cli, receive; sockaddr_in server, client; socklen_t len; // 用socklen_t代替unsigned int,更规范 if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Socket creation failed"); exit(-1); } server.sin_family = AF_INET; server.sin_port = htons(10000); server.sin_addr.s_addr = INADDR_ANY; memset(&(server.sin_zero), '\0', 8); len = sizeof(struct sockaddr_in); if((bind(sock, (struct sockaddr *)&server, len)) == -1) { perror("Bind failed"); exit(-1); } if((listen(sock, 5)) == -1) { perror("Listen failed"); exit(-1); } cout << "Server listening on port 10000..." << endl; while(1) { len = sizeof(struct sockaddr_in); cli = accept(sock,(struct sockaddr *)&client, &len); if(cli == -1) { perror("Accept failed"); continue; // 出错后继续监听,不要直接退出 } cout << "New client connected: " << inet_ntoa(client.sin_addr) << endl; // 用cli套接字接收数据,而不是sock receive = recv(cli, (void*)&UE2, sizeof(UE2), 0); if(receive == -1) { perror("Receive failed"); close(cli); continue; } else if(receive == 0) { cout << "Client disconnected" << endl; close(cli); continue; } // 转换字节序,把网络字节序转回主机字节序 UE2.imsi = ntohl(UE2.imsi); cout << "Received struct content:" << endl; cout << "Net: " << UE2.Net << endl; cout << "IMSI: " << UE2.imsi << endl; close(cli); } }
客户端(Client.cpp)
#include <arpa/inet.h> #include<iostream> #include <cstring> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<string> #include<stdio.h> using namespace std; struct UE { char Net[10]; int imsi; }; int main(int argc, char *argv[]) { if(argc != 3) { cout << "Usage: ./client <server_ip> <port>" << endl; exit(-1); } UE UE1; // 把字符串复制到char数组里 strncpy(UE1.Net, "4G", sizeof(UE1.Net)-1); UE1.Net[sizeof(UE1.Net)-1] = '\0'; // 确保字符串以结束符结尾 UE1.imsi = htonl(8649); // 用htonl处理32位int的字节序 int sock, send_bytes; struct sockaddr_in server; if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("Socket creation failed"); exit(-1); } server.sin_family = AF_INET; server.sin_port = htons(atoi(argv[2])); server.sin_addr.s_addr = inet_addr(argv[1]); memset(&(server.sin_zero), '\0', 8); if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == -1) { perror("Connect failed"); exit(-1); } // 发送结构体,检查发送字节数 send_bytes = send(sock, &UE1, sizeof(UE1), 0); if(send_bytes == -1) { perror("Send failed"); } else { cout << "Sent " << send_bytes << " bytes to Server:" << endl; cout << "Net: " << UE1.Net << ", IMSI: " << ntohl(UE1.imsi) << endl; } close(sock); return 0; }
关键修改点解释
- 替换
std::string为固定大小char数组:这样结构体的所有数据都存储在连续内存中,发送结构体时会把字符串的实际内容一起发出去,服务端能直接读取到"4G"。 - 使用正确的套接字通信:服务端必须用
accept返回的cli套接字来调用recv,这是和客户端建立的专属连接通道。 - 字节序处理:网络传输用大端字节序,主机可能是小端,所以用
htonl()把int转成网络字节序,服务端用ntohl()转回来,确保跨平台也能正确解析。 - 返回值检查:每次调用
send/recv都检查返回值,能及时发现通信错误,避免无效的后续操作。 - 修正循环逻辑:客户端去掉了错误的循环,直接发送一次数据,更简洁。
现在你可以编译运行这两个代码:
- 编译服务端:
g++ Server.cpp -o server,运行./server - 编译客户端:
g++ Client.cpp -o client,运行./client 127.0.0.1 10000
这样就能看到服务端正确接收到结构体内容啦~
内容的提问来源于stack exchange,提问作者Purdgr8
相关产品推荐
相关产品推荐

