手写一个 C++ TCP 服务器实现自定义协议
在网络编程中,我们常接触 UDP 和 TCP。UDP 基于数据报传输,发送即接收,边界清晰;而 TCP 基于字节流传输,没有消息边界。这意味着我们无法保证一次 read() 调用就能拿到完整的数据包。
TCP 字节流带来的挑战
客户端通过 connect 建立连接后,使用 write 将数据写入 socket 文件描述符。发送方只管写,至于何时真正发送到网络、分几次发,全由 TCP 内核控制。这导致接收端在读取时面临不确定性:
- 可能只读到半个报文
- 也可能一次性读到多个报文
这就好比想发微信道歉说'我不后悔我爱你',结果对方只收到了'我不后悔',误会就产生了。为了避免这种数据不一致,必须在应用层制定协议,明确数据的起止边界。
自定义应用层协议设计
为什么需要协议?
就像群聊里发消息,除了内容(如'哈哈哈'),还有昵称、头像、时间等元数据。这些数据需要序列化成一个整体字符串发送,接收后再反序列化解构。对于结构化数据(如计算器请求),同样需要定义格式。
协议格式
采用简单的长度头加分隔符方案:
len\n
content\n
例如发送 10 + 5,编码后为:
6\n10 + 5\n
这种格式让接收方能先读长度,再精确读取对应字节数的内容,彻底解决粘包问题。
核心代码实现
协议封装 (Encode/Decode)
我们需要两个函数来处理数据的序列化与解析。
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(0, left_pos);
len = std::(content_len);
(s.() < content_len.() + len + ) ;
*content = s.(left_pos + , len);
s.(, content_len.() + len + );
;
}


