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

C语言通过Socket发送结构体异常问题求助

Socket结构体发送异常排查与解决方案

问题现象

通过Socket发送结构体时出现以下异常:

  • 发送数组时连接正常,但发送简单结构体时,服务端仅能接收到Recv - 1 - 1
  • 客户端运行时修改结构体status为0(ID保持1),客户端显示已发送,但服务端接收不到数据(输出空白)
  • 改回ID=1、status=1时,服务端又能正常接收

客户端代码

void func()
{
    printf("Send - %i - %i\n", mRELAIS.ID, mRELAIS.status);
    send(sockfd,&mRELAIS,sizeof(struct RELAIS),0);
}

客户端输出:Send - 1 - 1 或 Send - 1 - 0

客户端结构体修改逻辑

if(temperature>(g_Value+g_PlusMinus) && relais[0]==0) 
{
    printf("%.1lf°C Relais wird 1 Soll %.1lf\n", temperature,(g_Value+g_PlusMinus)); 
    mRELAIS.ID=1;
    mRELAIS.status=1; 
    func();
}
else if(temperature<(g_Value-g_PlusMinus) && relais[0]==1) 
{
    printf("%.1lf°C Relais wird 0 Soll %.1lf\n", temperature,(g_Value-g_PlusMinus)); 
    mRELAIS.ID=1;
    mRELAIS.status=0; 
    func();
}

服务端代码

for (;;)
{
    recv(connfd, &mRELAIS, sizeof(struct RELAIS),0);
    printf("Recv - %i - %i\n", mRELAIS.ID, mRELAIS.status);
    ....
}

服务端输出:Recv - 1 - 1 或空白


核心解决步骤

1. 处理send/recv的部分传输问题

TCP是字节流协议,send和recv不一定会一次性传输完整的结构体数据,忽略返回值会导致数据不完整。必须循环调用直到数据全部发送/接收完成。

客户端修改:

void func()
{
    printf("Send - %i - %i\n", mRELAIS.ID, mRELAIS.status);
    ssize_t total_sent = 0;
    ssize_t send_len;
    const size_t relais_size = sizeof(struct RELAIS);
    
    while (total_sent < relais_size) {
        send_len = send(sockfd, ((char*)&mRELAIS) + total_sent, relais_size - total_sent, 0);
        if (send_len <= 0) {
            perror("send failed");
            break;
        }
        total_sent += send_len;
    }
}

服务端修改:

for (;;)
{
    struct RELAIS temp_relais;
    ssize_t total_recv = 0;
    ssize_t recv_len;
    const size_t relais_size = sizeof(struct RELAIS);
    
    // 循环接收直到获取完整结构体
    while (total_recv < relais_size) {
        recv_len = recv(connfd, ((char*)&temp_relais) + total_recv, relais_size - total_recv, 0);
        if (recv_len <= 0) {
            perror("recv failed");
            close(connfd);
            goto reset_connection;
        }
        total_recv += recv_len;
    }
    
    mRELAIS = temp_relais;
    printf("Recv - %i - %i\n", mRELAIS.ID, mRELAIS.status);
    // ... 其他业务逻辑
    
reset_connection:
    continue;
}

2. 确保两端结构体定义完全一致

客户端和服务端的struct RELAIS必须完全相同,包括成员类型、顺序,以及编译器内存对齐规则。如果两端内存布局不一致,接收后数据会错位。

示例统一结构体定义(两端都要用):

// 避免内存对齐差异,可添加编译选项强制对齐(如GCC的-fpack-struct)
struct RELAIS {
    int ID;
    int status;
    // 若有其他成员,必须两端完全一致
};

3. 解决TCP粘包问题(可选但推荐)

添加固定长度的数据包头部,明确数据包边界,避免粘包导致的解析错误。

客户端发送带长度的数据包:

void func()
{
    printf("Send - %i - %i\n", mRELAIS.ID, mRELAIS.status);
    int packet_len = sizeof(struct RELAIS);
    
    // 先发送数据包长度
    ssize_t sent_len = send(sockfd, &packet_len, sizeof(packet_len), 0);
    if (sent_len <= 0) {
        perror("send length failed");
        return;
    }
    
    // 再发送结构体数据
    ssize_t total_sent = 0;
    while (total_sent < packet_len) {
        sent_len = send(sockfd, ((char*)&mRELAIS) + total_sent, packet_len - total_sent, 0);
        if (sent_len <= 0) {
            perror("send data failed");
            break;
        }
        total_sent += sent_len;
    }
}

服务端接收带长度的数据包:

for (;;)
{
    int packet_len;
    ssize_t recv_len = recv(connfd, &packet_len, sizeof(packet_len), 0);
    if (recv_len <= 0) {
        perror("recv length failed");
        close(connfd);
        goto reset_connection;
    }
    
    struct RELAIS temp_relais;
    ssize_t total_recv = 0;
    while (total_recv < packet_len) {
        recv_len = recv(connfd, ((char*)&temp_relais) + total_recv, packet_len - total_recv, 0);
        if (recv_len <= 0) {
            perror("recv data failed");
            close(connfd);
            goto reset_connection;
        }
        total_recv += recv_len;
    }
    
    mRELAIS = temp_relais;
    printf("Recv - %i - %i\n", mRELAIS.ID, mRELAIS.status);
    // ... 其他业务逻辑
    
reset_connection:
    continue;
}

4. 同步客户端relais数组状态

客户端修改结构体的逻辑依赖relais[0]的状态,必须确保该数组状态与mRELAIS.status同步,否则会导致逻辑不触发或重复发送错误数据:

if(temperature>(g_Value+g_PlusMinus) && relais[0]==0) 
{
    printf("%.1lf°C Relais wird 1 Soll %.1lf\n", temperature,(g_Value+g_PlusMinus)); 
    mRELAIS.ID=1;
    mRELAIS.status=1; 
    relais[0] = 1; // 同步更新数组状态
    func();
}
else if(temperature<(g_Value-g_PlusMinus) && relais[0]==1) 
{
    printf("%.1lf°C Relais wird 0 Soll %.1lf\n", temperature,(g_Value-g_PlusMinus)); 
    mRELAIS.ID=1;
    mRELAIS.status=0; 
    relais[0] = 0; // 同步更新数组状态
    func();
}

内容的提问来源于stack exchange,提问作者S. Schlegel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:20:38