UDP客户端向服务器发送结构体异常问题排查
UDP结构体传输异常问题
问题描述
尝试通过UDP客户端向服务器发送自定义结构体数据时,仅int类型字段a值正确,double类型b和float类型c的值均为0。预期接收结果:
int a: 1 double b: 2.0 float c: 3.0
实际接收结果中b和c均为0。以下是完整代码实现:
UDPClient.cpp
#include "UDPClient.h" UDPClient::UDPClient(unsigned short port) { // UDP Client sends messages to UDP server on this port. this->port = port; // Create UDP socket file descriptor. // int socket(int domain, int type, int protocol); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { std::cerr << "ERROR: Socket creation failed. socket() system call returned an error." << std::endl; exit(EXIT_FAILURE); } // Zero memory of serv_addr struct. memset(&serv_addr, 0, sizeof(serv_addr)); // Configure server address structure. serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); } void UDPClient::send(const char* bytes) { // ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); sendto(sockfd, bytes, strlen(bytes), MSG_CONFIRM, (const sockaddr*)&serv_addr, sizeof(serv_addr)); std::cout << "Message Sent." << std::endl; }
UDPClient.h
#ifndef UDPCLIENT_H #define UDPCLIENT_H #include <iostream> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> class UDPClient { public: UDPClient(unsigned short); void send(const char*); private: unsigned short port; int sockfd; sockaddr_in serv_addr; }; #endif
sendData.cpp
#include "UDPClient.h" struct myStruct { int a; double b; float c; }; int main() { unsigned short port = 22842; UDPClient client(port); myStruct data; data.a = 1; data.b = 2.0; data.c = 3.0; const char* dataInBytes = reinterpret_cast<char*>(&data); client.send(dataInBytes); }
UDPServer.cpp
#include "UDPServer.h" UDPServer::UDPServer(unsigned short port) { // UDP Server binds to an address on this port. this->port = port; // Create UDP socket file descriptor. // socket(int domain, int type, int protocol); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { std::cerr << "ERROR: Socket creation failed. socket() system call returned an error." << std::endl; exit(EXIT_FAILURE); } // Zero memory of serv_addr struct. memset(&serv_addr, 0, sizeof(serv_addr)); // Configure server address structure. serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(port); // Bind the socket to the server address. // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); if (bind(sockfd, (sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) { std::cerr << "ERROR: Binding socket failed. bind() system call returned an error." << std::endl; exit(EXIT_FAILURE); } cli_len = sizeof(cli_addr); } char* UDPServer::receive() { // ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); int n = recvfrom(sockfd, (char*) buffer, MAXLINE, MSG_CONFIRM, (sockaddr*) &cli_addr, &cli_len); return buffer; }
UDPServer.h
#ifndef UDPSERVER_H #define UDPSERVER_H #include <iostream> #include <cstring> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define MAXLINE 1024 class UDPServer { public: UDPServer(unsigned short); char* receive(); private: unsigned short port; int sockfd; sockaddr_in serv_addr; sockaddr_in cli_addr; socklen_t cli_len; char buffer[MAXLINE]; }; #endif
receiveData.cpp
#include "UDPServer.h" struct myStruct { int a; double b; float c; }; int main() { unsigned short port = 22842; UDPServer server(port); myStruct data; char* bytes = server.receive(); memcpy(&data, bytes, sizeof(data)); std::cout << "int a: " << data.a << std::endl; std::cout << "double b: " << data.b << std::endl; std::cout << "float c: " << data.c << std::endl; }
问题根源
1. 发送长度计算错误(核心问题)
客户端UDPClient::send方法中使用strlen(bytes)计算发送数据长度,这是错误的:
strlen仅适用于以'\0'结尾的字符串,而结构体的二进制数据中,int a=1的二进制表示后会包含多个'\0'字节(比如32位int是0x01 0x00 0x00 0x00),strlen会在第一个'\0'处停止计数,导致实际只发送了1字节数据,double和float字段根本没被发送到服务器,所以接收端这两个字段的值是默认的0。
2. 潜在的结构体对齐与字节序问题
- 结构体在不同编译器、平台下可能存在内存对齐差异,导致两端结构体的内存布局不一致。
- 整数、浮点数的字节序(大端/小端)在不同平台可能不同,跨平台传输时会出现解析错误。
解决方案
步骤1:修复发送长度计算
修改UDPClient的send方法,传入数据的实际大小,而非用strlen:
- 修改
UDPClient.h中的send函数声明:
void send(const char* bytes, size_t length);
- 修改
UDPClient.cpp中的send实现:
void UDPClient::send(const char* bytes, size_t length) { sendto(sockfd, bytes, length, MSG_CONFIRM, (const sockaddr*)&serv_addr, sizeof(serv_addr)); std::cout << "Message Sent." << std::endl; }
- 在
sendData.cpp中调用时传入结构体大小:
client.send(dataInBytes, sizeof(myStruct));
步骤2:优化接收端处理
服务器的receive方法应返回实际接收的字节数,避免内存越界或解析错误:
- 修改
UDPServer.h中的receive函数声明:
ssize_t receive(char* out_buffer, size_t max_len);
- 修改
UDPServer.cpp中的receive实现:
ssize_t UDPServer::receive(char* out_buffer, size_t max_len) { return recvfrom(sockfd, out_buffer, max_len, MSG_CONFIRM, (sockaddr*) &cli_addr, &cli_len); }
- 在
receiveData.cpp中处理接收结果:
int main() { unsigned short port = 22842; UDPServer server(port); myStruct data{}; // 初始化结构体为0 char buffer[MAXLINE]; ssize_t recv_len = server.receive(buffer, MAXLINE); if (recv_len == sizeof(myStruct)) { memcpy(&data, buffer, sizeof(data)); std::cout << "int a: " << data.a << std::endl; std::cout << "double b: " << data.b << std::endl; std::cout << "float c: " << data.c << std::endl; } else { std::cerr << "ERROR: Received incomplete data." << std::endl; } }
步骤3:解决结构体对齐与字节序问题(可选,跨平台场景必备)
如果需要跨平台传输,建议:
- 强制结构体对齐:使用编译器指令固定对齐方式,比如GCC的
__attribute__((packed)):
struct myStruct { int a; double b; float c; } __attribute__((packed));
- 统一字节序:将结构体中的数值转换为网络字节序(大端)后发送,接收端再转换为主机字节序。注意浮点数需要先转为字节数组再处理,或者使用序列化库(如Protobuf、FlatBuffers)进行跨平台序列化。
内容的提问来源于stack exchange,提问作者user20278411
相关产品推荐
相关产品推荐

