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
相关产品推荐
相关产品推荐

