day8-解决socket粘包之大数据
问题概述
之前我们通过在服务器端发送数据到客户端之前,先计算发送数据的大小,再发送给客户端,而客户端可以根据服务器端发来的数据总大小,进行多次循环接收,直到全部接收完成。到这里程序运行似乎没什么问题,但是我们在代码运行测试的过程中,也会看到类似以下的问题。
服务器端
import socket,os
server = socket.socket()
server.bind(("localhost",9981))
server.listen()
while True:
conn,addr = server.accept()
print("new conn:",addr)
while True:
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data.decode())
cmd_res = os.popen(data.decode()).read() #接收的指令decode成str,命令的执行结果也是str
conn.send(str(len(cmd_res.encode())).encode()) #发送执行结果的大小给客户端
if len(cmd_res) == 0:
cmd_res= "cmd没有正确输出。。。。"
conn.send(cmd_res.encode())
print("发送完成!")
客户端
import socket client = socket.socket() client.connect(("localhost",9980)) while True: cmd_input = input(">>:") if len(cmd_input) == 0: continue client.send(cmd_input.encode()) recv_data_total_size = client.recv(1024).decode() #接收命令执行结果的总大小 print(recv_data_total_size) recv_size = 0 recv_data = b"" while recv_size < int(recv_data_total_size): data = client.recv(1024) recv_size+=len(data) #每次接收到的有可能小于1024,所以必须使用len判断 recv_data += data else: print("接收完成,总共接收的大小为:",recv_size) print(recv_data.decode())
执行过程
服务器端
客户端
可以看到客户端结果返回时,出现ValueError,细看可以发现因为服务器端连续发送两次使2次的结果粘在一起发送到客户端,客户端接收到数据后,在while进行判断时,发生了ValueError的错误,这是为什么呢?
解析:上图在服务器端,首先客户端发来的命令被服务器端执行并分别返回结果的大小和内容,大小和内容都被存储在I/O缓冲区等待被发送。当我们手动conn.send强制缓冲区超时发送,因为连续调用2次send的动作(紧挨着),在这种情况下,I/O缓冲区就会把大小和内容数据合并成一条数据发送给客户端(把2次的手动强制缓冲区超时当做1次),这就导致在网络开发过程中粘包的问题。这种粘包的情况不是每次都能发生,但是我们如何去避免粘包的问题呢?
解决方案
1.使用sleep()方法
#输出 >>:pwd 42 接收完成,总共接收的大小为: 42 /Users/huwei/PycharmProjects/s14/module_4 >>:ls 42 接收完成,总共接收的大小为: 42 socket_client_ssh.py socket_server_ssh.py
解析:使用sleep将2次send强制缓冲区超时相隔0.5s,成功解决了粘包的问题,但是我们在客户端输入命令后,要等0.5s才能看到结果,影响了代码的实时性能。
2.服务器/客户端中加入交互
优化服务器端:等待客户端的确认信息
优化客户端:发送确认信息到服务器端