粘包问题以及解决方法
一、粘包问题
1、问题一:无法确认对方发送过来数据的大小,对数据接收有影响
server.py文件内容:
""" 先启动套接字服务端 注意: 客户端一次发送,服务端先一次接收,再发送 """ import socket import subprocess server = socket.socket() server.bind(("127.0.0.1", 9527)) server.listen(5) while True: conn, addr = server.accept() print(addr) while True: try: # 从内存中获取数据 data = conn.recv(1024) if len(data) == 0: continue data = data.decode("utf-8") if data == "q": break # 调用subprocess,对终端进行操作,并获取操作后正确或错误的结果 # 接收转码后的字符串 obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 将结果交给result变量 result = obj.stdout.read() + obj.stderr.read() print(result.decode("gbk")) # 将结果返回给客户端 conn.send(result) except Exception as e: print(e) break conn.close()
client.py文件内容:
""" 启动服务端后再启动客户端 """ import socket client = socket.socket() client.connect(("127.0.0.1", 9527)) while True: cmd = input("请输入服务端命令:") client.send(cmd.encode("utf-8")) data = client.recv(1024) # 这里只接受到1024字节数据,可以调整,但是对内存有影响 if len(data) == 0: continue if data == "q": break print(data.decode('gbk'))
server.py执行结果:
client.py执行结果:
2、问题二:在发送数据间隔短并且数据量小的情况下,会将所有数据一次性传入
server.py文件内容:
""" 先启动套接字服务端 注意: 客户端一次发送,服务端先一次接收,再发送 """ import socket server = socket.socket() server.bind(("127.0.0.1", 9527)) server.listen(5) conn, addr = server.accept() data = conn.recv(1024) print(data)
client.py文件内容:
""" 启动服务端后再启动客户端 """ import socket client = socket.socket() client.connect(("127.0.0.1", 9527))
'''
发送三次请求,理论上结果应该是
b"hello"
b"hello"
b"hello"
''' client.send(b"hello") client.send(b"hello") client.send(b"hello")
server.py执行结果:
3、解决粘包问题(struct模块)
1、struct模块是什么
struct模块是一个python内置模块,他可以将固定长度的数据,打包成固定格式的长度
" i "模式:可以将数据打包成 4 个bytes
2、struct模块的作用
可以将真实数据,做成一个固定长度的报头,客户端发送给服务端,服务端可以接受报头,然后对报头进行解包,获取真实数据的长度,进行接收即可
3、struct模块的使用
import struct data = b'11111111111111' # 打包制作报头 header = struct.pack("i", len(data)) print(header) # 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度 res = struct.unpack("i", header)[0] print(res)
执行结果:
b'\x0e\x00\x00\x00' 14
4、粘包问题解决方法
只要确认对方数据的大小(长度),根据大小进行接收即可解决
- 无论哪一端先发送数据(例 客户端先行发送数据)
- 客户端
- 先制作报头并发送 struct.pack()
- 发送真实数据
- 服务端:
- 接收报头,并解包获取真实数据长度 struct.unpack()
- 根据真实数据长度 接收真实数据
server.py文件内容:
import socket import subprocess import struct server = socket.socket() server.bind(("127.0.0.1", 8001)) server.listen(5) while True: conn, addr = server.accept() # print(addr) while True: try: # 获取客户端传过来的报头 client_headers = conn.recv(4) # 解包获取真实数据长度 data_len = struct.unpack("i", client_headers)[0] # 准备接收真实数据 data = conn.recv(data_len) if len(data) == 0: continue data = data.decode("utf-8") if data == "q": break # 调用subprocess,对终端进行操作,并获取操作后正确或错误的结果 # 接收转码后的字符串 obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 将结果交给result变量 result = obj.stdout.read() + obj.stderr.read() # 服务端做一个报头发送给客户端 server_headers = struct.pack('i', len(result)) # 传送报头数据 conn.send(server_headers) # 待客户端确认长度后,发送真实数据返回给客户端 conn.send(result) except Exception as e: print(e) break conn.close()
client.py文件内容:
import socket import struct client = socket.socket() client.connect(("127.0.0.1", 8001)) while True: cmd = input("请输入服务端命令:") cmd_bytes = cmd.encode("utf-8") # 做一个报头 client_headers = struct.pack("i", len(cmd_bytes)) # 想服务端传送报头 client.send(client_headers) # 待服务端确认数据长度后,发送真实数据 client.send(cmd_bytes) # 获取服务端的传过来的报头 server_headers = client.recv(4) # 解包获取数据的真实长度 data_len = struct.unpack('i', server_headers)[0] # 准备接受真实数据 data = client.recv(data_len) if len(data) == 0: continue if data == "q": break print(data.decode('gbk'))
可自行测试。