必须显式发送长度并分步接收,因TCP是流式协议且recv()不保证一次收完;数组需序列化,避免裸指针和未定义行为。

发送原始数组时为什么收端数据错乱或截断
直接用 send() 发送数组指针,本质是发内存块,但接收端不知道长度、类型、字节序,更不保证一次收完。常见现象是 recv() 返回值小于预期,或收到乱码——这不是网络丢包,而是 TCP 流式特性 + 应用层协议缺失导致的。
实操建议:
- 必须显式发送长度:先发 4 字节
uint32_t(用htonl()转网络序),再发数组内容 - 接收端分两步:先循环收够 4 字节,解析出长度
n;再循环收够n字节(recv()可能返回 1~n 之间任意值) - 数组元素若含非 POD 类型(如
std::string、指针),不能直接 memcpy —— 必须序列化
用 std::vector<uint8_t> 替代裸数组更安全
裸数组名退化为指针,丢失大小信息;而 std::vector 可随时通过 .data() 和 .size() 获取有效地址与长度,避免手动计算偏移出错。
示例发送逻辑:
立即学习“C++免费学习笔记(深入)”;
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0x04};
uint32_t len_net = htonl(static_cast<uint32_t>(data.size()));
send(sock, reinterpret_cast<const char*>(&len_net), sizeof(len_net), 0);
send(sock, reinterpret_cast<const char*>(data.data()), data.size(), 0);
注意:reinterpret_cast<const char*> 是必需的,因为 send() 第二个参数要求 const char*;别用 static_cast,会触发未定义行为。
接收端必须处理 recv() 返回值为 0 或 -1 的情况
recv() 返回 0 表示对端已关闭连接(FIN),返回 -1 且 errno == EAGAIN/EWOULDBLOCK 表示无数据可读(非阻塞模式下),其他负值通常是错误(如 ECONNRESET)。
关键点:
- 不能假设
recv()一次收完指定字节数 —— 必须用 while 循环累加 - 阻塞 socket 下,
recv()在连接关闭前不会返回 0;但程序异常退出、网线拔掉等情况会导致提前返回 0 或 -1 - 如果用
select()或epoll(),需检查可读事件是否真实有数据,避免空转
跨平台收发 float/double 数组要小心字节序和 ABI 差异
虽然 float 和 double 在主流平台都是 IEEE 754,但直接发裸内存仍有风险:ARM 和 x86 都是小端,但某些嵌入式平台可能是大端;更重要的是,不同编译器对结构体填充、对齐可能不同。
稳妥做法:
- 发前用
memcpy拷贝到uint8_t缓冲区,再按字节发送 - 收后同样用
memcpy拷贝回目标变量(禁止指针强转,防止 strict aliasing 违规) - 如需高可靠,改用文本(JSON/CSV)或标准序列化协议(Protocol Buffers)
容易被忽略的是:即使两端都是小端,若数组长度未对齐到 4/8 字节边界,某些平台的未对齐访问会触发 SIGBUS —— 所以始终用 memcpy 中转。


















