You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:解决结构体对齐与字节序问题(可选,跨平台场景必备)

如果需要跨平台传输,建议:

  1. 强制结构体对齐:使用编译器指令固定对齐方式,比如GCC的__attribute__((packed)):
struct myStruct {
    int a;
    double b;
    float c;
} __attribute__((packed));
  1. 统一字节序:将结构体中的数值转换为网络字节序(大端)后发送,接收端再转换为主机字节序。注意浮点数需要先转为字节数组再处理,或者使用序列化库(如Protobuf、FlatBuffers)进行跨平台序列化。

内容的提问来源于stack exchange,提问作者user20278411

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 20:01:44