Python全栈-网络编程-TCP粘包

时间:2023-03-09 05:41:55
Python全栈-网络编程-TCP粘包

一、什么是TCP粘包

  C/S架构下,接收方不知道每个消息的发送间隙、也不知道每次应该提取多少个字节的数据,与此同时,TCP是面向连接的,面向流的,收发两端都要有,因此发送端为了将多个发往接收端的数据包更高效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个发送给接收端。此时接收端无法分辨出来,必须提供合理的拆包机制,即面向流的通信是无消息保护边界的。
  除此之外,因为TCP是基于流的,所以收发的消息不能为空,需要发送、接收端添加空消息处理机制,防止程序卡住。

二、处理思路

   粘包现象主要是因为发送端没有确切的发送间隔以及发送数据包的大小,而接收端更不知道发送算发送了多少个数据包以及大小,只能来多少接收多少。
   解决方法:
    自定义报头,发送端发送数据之前,先将自定义的报头(数据包大小等信息)发送给接收端,接收端明确每个数据包的大小进行依次接收。

三、实现方式

方法一:

 # 客户端
import socket
import struct IP = '127.0.0.1'
PORT = 8080
bufsize = 1024 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client_socket.connect((IP,PORT)) while True:
cmd = input('cmd>>>').strip()
if len(cmd) == 0:continue
elif cmd == 'q':break client_socket.send(cmd.encode('utf-8')) # 1.接收固定报头
header = client_socket.recv(4) # 2.解析报头
total_size = struct.unpack('i',header)[0]
print(total_size) # 3.根据包头接收真实数据
recv_size = 0
# 保存接收的数据(接收到的是byte类型)
res_data = b''
while recv_size < total_size:
recv_data = client_socket.recv(1024)
res_data += recv_data
recv_size += len(recv_data) print(recv_data.decode('gbk')) client_socket.close() # 服务端
import socket
import subprocess
import struct IP = '127.0.0.1'
PORT = 8080
bufsize = 1024 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.bind((IP,PORT),)
tcp_socket.listen(5) while True:
conn,addr = tcp_socket.accept()
print('客户端:',addr) while True:
try:
cmd = conn.recv(bufsize)
res = subprocess.Popen(cmd.decode('utf-8'),shell=True
,stdin=subprocess.PIPE
,stdout=subprocess.PIPE
,stderr=subprocess.PIPE)
stderr = res.stderr.read()
stdout = res.stdout.read() # 1.制作固定长度的报头
total_size = len(stdout) + len(stderr)
header = struct.pack('i',total_size) # 2.发送报头
conn.send(header) # 3.发送真实数据
conn.send(stderr)
conn.send(stdout)
except ConnectionResetError:
break
conn.close()
tcp_socket.close()

解决粘包问题

方法二:

 # 客户端
import socket
import struct
import json IP = '127.0.0.1'
PORT = 8080
bufsize = 1024 client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client_socket.connect((IP,PORT)) while True:
cmd = input('cmd>>>').strip()
if len(cmd) == 0:continue
elif cmd == 'q':break client_socket.send(cmd.encode('utf-8')) # 1.接收包头长度
header_size = struct.unpack('i',client_socket.recv(4))[0] # 2.接收报头
header_bytes = client_socket.recv(header_size) # 3.解析报头
header_json = header_bytes.decode('utf-8')
header_dic = json.loads(header_json)
# print(header_dic) total_size = header_dic['total_size'] # 3.根据包头接收真实数据
recv_size = 0
# 保存接收的数据(接收到的是byte类型)
res_data = b''
while recv_size < total_size:
recv_data = client_socket.recv(1024)
res_data += recv_data
recv_size += len(recv_data) print(recv_data.decode('gbk')) client_socket.close() # 服务端
import socket
import subprocess
import struct
import json IP = '127.0.0.1'
PORT = 8080
bufsize = 1024 tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.bind((IP,PORT),)
tcp_socket.listen(5) while True:
conn,addr = tcp_socket.accept()
print('客户端:',addr) while True:
try:
cmd = conn.recv(bufsize)
res = subprocess.Popen(cmd.decode('utf-8'),shell=True
,stdin=subprocess.PIPE
,stdout=subprocess.PIPE
,stderr=subprocess.PIPE)
stderr = res.stderr.read()
stdout = res.stdout.read() # 1.制作固定长度的报头
header_dic = {
'total_size':len(stdout) + len(stderr),
'md5':'123sssss222',
'filename':'120.txt'} header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8') # 2.发送报头的长度
total_size = len(header_bytes)
conn.send(struct.pack('i',total_size)) # 发送报头
conn.send(header_bytes) # 3.发送真实数据
conn.send(stderr)
conn.send(stdout)
except ConnectionResetError:
break
conn.close()
tcp_socket.close()

解决粘包问题