黏包现象和解决黏包的方法
一, 缓冲区: 将程序和网络解耦
输入缓冲区
输出缓冲区
二, sunbprocess模块
import subprocess sub_obj = subprocess.Popen( 'ipconfig', #系统指令 shell=True, #固定 stdout=subprocess.PIPE, #标准输出 PIPE 管道,保存着指令的执行结果 stderr=subprocess.PIPE )#标准错误输出 a = sub_obj.stdout.read() #执行"ipconfig"命令的结果,且a为字节类型 b = sub_obj.stderr.read() #错误输出,但"ipconfig"命令可执行,所以b为空 print('正确输出',a.decode('gbk')) #因为操作系统的编码为"gbk",所以a字节应该用"gbk"来解码 print('错误输出',b.decode('gbk')) #如果命令不可以被执行,b.decode('gbk')会打印 不是内部或外部命令...
三, 两种黏包现象:
1 连续的小包可能会被优化算法给组合到一起进行发送
2 第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱(看代码)
黏包的服务端
import socket import subprocess server = socket.socket() ip_port = ("127.0.0.1",8001) server.bind(ip_port) server.listen() conn,add = server.accept() while 1 : from_client = conn.recv(1024).decode("utf-8") print(from_client) sub_obj = subprocess.Popen( from_client, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) std_msg = sub_obj.stdout.read() print("执行指令的长度:",len(std_msg)) conn.send(std_msg)
黏包的客户端
import socket client = socket.socket() client.connect(('127.0.0.1',8001)) while 1: cmd = input('请输入指令:') client.send(cmd.encode('utf-8')) server_cmd_result = client.recv(1024) print(server_cmd_result.decode('gbk'))
当客户端输入"ipconfig/all"之后,服务端发送给客户端的内容最多为1024字节,不能完全发过来.当客户端再输入一个"dir"命令后,之前没发完的消息会继续传给客户端,这就是黏包
解决黏包现象有两种方案
方案一:由于双方不知道对方发送数据的长度,导致接收的时候,可能接收不全,或者多接收另外一次发送的信息内容,所以在发送真实数据之前,要先发送数据的长度,接收端根据长度来接收后面的真实数据,但是双方有一个交互确认的过程
方案二: (重点)
Struct模块,
打包:struct.pack(‘i’,长度)
解包:struct.unpack(‘i’,字节) ,解包出来是元组,需要拿到元组的第0项索引的数据,就是之前的长度
解决黏包现象的服务端
import socket import subprocess import struct server = socket.socket() ip_port = ("127.0.0.1",8003) server.bind(ip_port) server.listen() conn,add = server.accept() while 1 : # 收到客户端发来的消息,并进行解码 from_client = conn.recv(1024).decode("utf-8") print(from_client) # 把收到的消息,放在"subprocess"模块中执行"Popen"方法(执行这条消息的指令) sub_obj = subprocess.Popen( from_client, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE ) # 得到执行命令的结果,该结果为字节 std_msg = sub_obj.stdout.read() #打印这个结果(字节)的长度 print("执行指令的长度:",len(std_msg)) # 把这个长度在"struct"模块中执行"pack"方法,"i"参数是把数字编译成4位字节(打包) dabao = struct.pack("i",len(std_msg)) # 把执行指令的结果,前面加上这个结果的长度(4位字节),(把打包"dabao"嵌在真实结果的包头)发送到客户端 conn.send(dabao+std_msg)
解决黏包现象的客户端
import socket import struct client = socket.socket() client.connect(('127.0.0.1',8003)) while 1: cmd = input('请输入指令:') # 把输入的指令进行编码,传到服务端端 client.send(cmd.encode('utf-8')) # 收到服务端发过来的消息(4个字节),其实就是服务端打包的执行命令的真实内容的长度. server_len_a = client.recv(4) # 在"struct"模块中执行"unpack"方法,把收到的4个字节解包 # 取出该解包之后的元组中的索引为0的元素,该元素就是执行命令的(字节的)长度 # 重点:解包出来的是元组,且只有一个元素 server_len = struct.unpack('i',server_len_a)[0] # 再次收到服务端发过来的消息,"recv"后面的参数为执行命令的(字节的)长度 server_msg = client.recv(server_len) # 因为操作系统编码为"gbk",所以执行命令的(字节类型)解码为"gbk" print(server_msg.decode('gbk'))