TCP拆包粘包这种说法对吗

对也不对。本质上对于定义的问题。代码使用方认为在应用层发送数据包时数据包是有边界的,因此在接收方时也应该读到边界。但是实际上TCP是基于字节流的,因此所有通过TCP传输的数据都被视作一个字节流。TCP只保证传输的内容的顺序性,但是应用是否应该将接收到的信息视作是同一个或者不同的包,TCP是管不着的。举个例子,当接收方使用某种带Buffer的机制读取时,就会读到连续的内容,而没有边界,从而可能导致解析发生错误。或者说换种说法,应用层需要自己定义协议内容,确保解析是正确的。

让我们来看看HTTP是怎么处理的。HTTP约定使用\r\n来区分Header和Body,此外在Header中附加的Content-Length确定了客户端应读取的内容长度。以下是一段示例代码。

import socket

def send_http_request(host, port, path):
# 创建 socket 对象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接服务器
sock.connect((host, port))

# 发送 HTTP 请求
request = f"GET {path} HTTP/1.1\r\nHost: {host}\r\n\r\n"
sock.sendall(request.encode('utf-8'))

# 接收 HTTP 响应
response = b''
while True:
chunk = sock.recv(4096)
if not chunk:
break
response += chunk

# 解析 HTTP 响应
status_line, headers, body = response.split(b'\r\n\r\n')

# 获取 Content-Length
content_length = None
for header in headers.split(b'\r\n'):
if header.startswith(b'Content-Length:'):
content_length = int(header.split(b':')[1].strip())
break

# 打印 HTTP 响应状态码
print(status_line.decode('utf-8'))

# 打印 HTTP 响应头
for header in headers.split(b'\r\n'):
if header:
print(header.decode('utf-8'))

# 读取 HTTP 响应正文
if content_length is not None:
body = body[:content_length]
print(body.decode('utf-8'))
else:
print('Content-Length header not found')


# 使用示例
send_http_request('www.example.com', 80, '/')

参考: