TCP 网络编程中的粘包问题与自定义协议设计
在网络编程中,UDP 基于数据报传输,天然具备消息边界。但 TCP 不同,它是字节流协议,没有明确的消息界限。这意味着发送方写入的数据,接收方可能一次读取一部分,也可能多次读取才凑齐一条完整消息。
为什么需要应用层协议?
TCP 通信时,客户端通过 connect 建立连接后,使用 write 将数据写入 socket 文件描述符。内核负责何时发送,而应用层只负责写入。这导致接收端无法直接知道一条消息的结束位置。
想象一下,如果发送方先发了一个整数,接着发一个浮点数,而接收方按字符串去解析,数据就会错位。因此,一旦涉及结构化数据,必须制定应用层协议来界定消息边界。

TCP 主要负责控制发送时机、数据量及错误处理,但具体的业务数据格式(如序列化后的结构)需由我们定义。否则,接收端面对连续的字节流,就像收到一段断断续续的语音,难以还原原意。
自定义应用层协议设计
协议格式
为了解决粘包和半包问题,我们采用'长度 + 内容'的定长头部方案:
len\n
content\n
例如,发送 10 + 5,实际传输的是:
6\n10 + 5\n
这种格式让接收方能先读取长度,再精确截取对应字节的内容。
协议封装实现
Encode(封装报文)
std::string Encode(std::string &content) {
std::string s;
size_t len = content.size();
s += std::to_string(len);
s += "\n";
s += content;
s += "\n";
return s;
}
Decode(解析报文)
解码函数需要完成三件事:判断头部是否完整、校验数据长度、提取内容并移除已读部分。
bool Decode(std::string &s, std::string *content) {
size_t left_pos = s.find("\n");
if (left_pos == std::string::npos) return false;
std::string content_len = s.substr(, left_pos);
len = std::(content_len);
(s.() < content_len.() + len + ) ;
*content = s.(left_pos + , len);
s.(, content_len.() + len + );
;
}


