一、粘包问题
问题1: 无法确认对方发送过来数据的大小。
import socket client = socket.socket() client.connect( ('127.0.0.1', 9000) ) while True: cmd = input('客户端输入的内容: ') client.send(cmd.encode('utf-8')) data = client.recv(19190) print(len(data)) print(data.decode('gbk'))
import socket import subprocess server = socket.socket() server.bind(('127.0.0.1',9000)) server.listen(5) while True: conn,addr = server.accept() print(addr) while True: try: cmd = conn.recv(10) if len(cmd) == 0: continue cmd = cmd.decode('utf-8') #utf8 if cmd == 'q': break #调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果 obj = subprocess.Popen( #cmd接受的是解码后的字符串 cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) #结果交给result变量名 result = obj.stdout.read()+obj.stderr.read() print(len(result)) print(result.decode('gbk')) #windows系统下默认编码gbk #将结果返回给客户端 conn.send(result) except Exception as e: print(e) break conn.close()
问题2: 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送。
import socket client = socket.socket() client.connect( ('127.0.0.1', 9000) ) client.send(b'hello') client.send(b'hello') client.send(b'hello')
import socket server = socket.socket() server.bind( ('127.0.0.1', 9000) ) server.listen(5) conn, addr = server.accept() data = conn.recv(5) print(data) # b'hello' data = conn.recv(1024) print(data) # b'hello' data = conn.recv(1024) print(data) # b'hello'
二、粘包问题的解决方案:
粘包问题的解决方案: 确认对方数据的大小。
这里需要用 struct模块
struct是什么?
是一个python内置的模块,它可以将固定长度的数据,打包成固定格式的长度。
固定格式:如 “ i ” 模式
i : 4
struct作用:
可以将真实数据,做成一个固定长度的报头,客户端发送给服务器,服务器可以接受报头,然后对报头进行解包,获取真实数据的长度,进行接收即可
import struct data = b'1111111111111111' print(len(data)) #16 #打包制作报头 header = struct.pack('i',len(data)) print(header) #b'x10x00x00x00' print(len(header)) #4 #解包获取真实数据长度 --->得到一个元组,元组中第一个值是真实数据的长度 res = struct.unpack('i',header)[0] print(res) #16
无论哪一端先发送数据
客户端
- 1) 先制作报头,并发送 (struct)
- 2) 发送真实数据
服务端:
- 1) 接收报头,并解包获取 真实数据长度
- 2) 根据真实数据长度 接收真实数据
recv(真实数据长度)
简单版:
import socket import struct client = socket.socket() client.connect(('127.0.0.1', 9000)) while True: cmd = input('客户端输入的内容: ') cmd_bytes = cmd.encode('utf-8') header = struct.pack('i',len(cmd_bytes)) #做一个报头 print(len(header)) #打印报头的长度 client.send(header) #发送报头 client.send(cmd_bytes) #待服务端确认长度后,发送真实数据长度 headers = client.recv(4) #接受服务端的报头 data_len = struct.unpack('i',headers)[0] #解包 result = client.recv(data_len) #接受服务器返回的真实数据的长度 print('接受服务器返回的真实数据的长度',len(result)) print(result.decode('gbk'))
import socket import subprocess import struct server = socket.socket() server.bind(('127.0.0.1',9000)) server.listen(5) while True: conn,addr = server.accept() print(addr) while True: try: header = conn.recv(10) #获取客户端传过来的报头 data_len = struct.unpack('i',header)[0] #解包获取真实数据的长度 cmd = conn.recv(data_len) #准备接受真实数据 if len(cmd) == 0: continue cmd = cmd.decode('utf-8') if cmd == 'q': break #调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果 obj = subprocess.Popen( #cmd接受的是解码后的字符串 cmd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) result = obj.stdout.read()+obj.stderr.read() #获取结果 print('发送给服务端返回的真实数据的长度', len(result)) header = struct.pack('i', len(result)) #做报头 print(len(header)) conn.send(header) #发送报头给客户端 conn.send(result) #将结果返回给客户端 except Exception as e: print(e) break conn.close()
序列化版:
import socket,json import struct client = socket.socket() client.connect(('127.0.0.1',9000)) while True: movie_name = input('请输入上传的电影名字:') #伪装电影的真实数据 movie = 1000000 send_dic ={'movie_name':movie_name, 'movie':movie} #序列化 json = json.dumps(send_dic) print(json) print(json.encode('utf-8')) print(len(json.encode('utf-8'))) json_bytes = json.encode('utf-8') #做一个报头 header = struct.pack('i',len(json_bytes)) #先发送报头 client.send(header) #再发送真实数据 client.send(json_bytes)
import socket,json import struct server = socket.socket() server.bind(('127.0.0.1',9000)) server.listen(5) while True: conn,addr = server.accept() while True: try: #获取客户端传过来的报头 header = conn.recv(4) #解包获取真实数据的长度 json_len = struct.unpack('i',header)[0] #接受json(dic)的真实数据 json_bytes_data = conn.recv(json_len) #将bytes类型数据转为json数据类型 json_data = json_bytes_data.decode('utf-8') #反序列化 json--->dict back_dic = json.loads(json_data) print(back_dic) print(back_dic.get('movie')) except Exception as e: print(e) break conn.close()