如何对通过TCP协议发送的数据进行标记?
我完全懂你的困惑——HTTP本身是基于TCP的,但它帮我们封装好了请求头、内容长度这些规则,所以我们不用操心数据边界的问题。但直接用TCP的话,就得自己搞定「怎么让对方知道一段数据从哪开始到哪结束」,这才是核心问题。
下面是几种工业界实际在用的解决方案,帮你搞定数据标记的问题:
1. 固定长度法
如果你的数据长度是固定的,这是最简单的方案。比如约定每次发送的数据都是1024字节,不足的部分用空字节或者特定填充符补全。接收方每次固定读取指定长度的字节即可。
示例代码(Python):
# 发送端:将数据填充到16字节长度 data = "hello tcp".encode() padded_data = data.ljust(16, b'\x00') # 用空字节填充 sock.sendall(padded_data) # 接收端:固定读取16字节,再去掉填充符 received = sock.recv(16).strip(b'\x00').decode()
2. 长度前缀法(最推荐)
这是处理可变长度数据的标准方案,也是HTTP等协议底层常用的思路。发送时先传一个固定长度的“长度标识”(比如4字节的整数),告诉接收方接下来要接收的数据有多长;接收方先读取这个长度,再根据长度读取对应字节数的实际内容。
这种方法灵活可靠,特别适合发送JSON这类长度不固定的数据。
示例代码(Python):
import struct # 发送端:先打包数据长度,再发送JSON内容 json_content = '{"name": "Alice", "age": 30}'.encode() # 用struct把长度打包成4字节大端整数(!I表示网络字节序的无符号整数) length_prefix = struct.pack('!I', len(json_content)) sock.sendall(length_prefix + json_content) # 接收端:先读取长度前缀,再读取对应长度的数据 prefix = sock.recv(4) data_length = struct.unpack('!I', prefix)[0] received_json = sock.recv(data_length).decode()
3. 分隔符法
约定一个不会出现在实际数据中的特殊分隔符(比如自定义的###END###,或者HTTP里的\r\n\r\n),发送方在数据末尾加上分隔符;接收方持续读取字节,直到遇到分隔符为止,再截断分隔符得到原始数据。
⚠️ 注意:一定要确保分隔符不会被数据本身包含,否则会导致接收方提前截断数据,出现错误。
示例代码(Python):
# 发送端:在JSON数据后添加分隔符 json_content = '{"name": "Bob"}'.encode() sock.sendall(json_content + b'###END###') # 接收端:循环读取直到找到分隔符 buffer = b'' while b'###END###' not in buffer: chunk = sock.recv(1024) if not chunk: # 连接断开 break buffer += chunk # 分割出原始数据 received_json = buffer.split(b'###END###')[0].decode()
其实你熟悉的HTTP协议,就是用了「长度前缀(Content-Length头)」或者「分块传输(Transfer-Encoding: chunked)」的方式来标记数据边界的。如果要发送JSON,优先选长度前缀法,既简单又不容易出问题。
内容的提问来源于stack exchange,提问作者Nicke7117

