峰哥解決粘包的方式
阿新 • • 發佈:2018-08-16
header enc pip cli 一個 長度 www. 解決 json
峰哥解決粘包的方法
為字節流加上自定義固定長度報頭,報頭中包含字節流長度,然後一次send到對端,對端在接收時,先從緩存中取出定長的報頭,然後再取真實數據
struct模塊
該模塊可以把一個類型,如數字,轉成固定長度的bytes
>> > struct.pack(‘i‘, 1111111111111)
。。。。。。。。。
struct.error: ‘i‘
format
requires - 2147483648 <= number <= 2147483647 # 這個是範圍
import json, struct # 假設通過客戶端上傳1T:1073741824000的文件a.txt# 為避免粘包,必須自定制報頭 header = {‘file_size‘: 1073741824000, ‘file_name‘: ‘/a/b/c/d/e/a.txt‘, ‘md5‘: ‘8f6fbf8347faa4924a76856701edb0f3‘} # 1T數據,文件路徑和md5值 # 為了該報頭能傳送,需要序列化並且轉為bytes head_bytes = bytes(json.dumps(header), encoding=‘utf-8‘) # 序列化並轉成bytes,用於傳輸 # 為了讓客戶端知道報頭的長度,用struck將報頭長度這個數字轉成固定長度:4個字節 head_len_bytes = struct.pack(‘i‘, len(head_bytes)) # 這4個字節裏只包含了一個數字,該數字是報頭的長度 # 客戶端開始發送 conn.send(head_len_bytes) # 先發報頭的長度,4個bytes conn.send(head_bytes) # 再發報頭的字節格式 conn.sendall(文件內容) # 然後發真實內容的字節格式 # 服務端開始接收 head_len_bytes = s.recv(4) # 先收報頭4個bytes,得到報頭長度的字節格式 x = struct.unpack(‘i‘, head_len_bytes)[0] # 提取報頭的長度 head_bytes= s.recv(x) # 按照報頭長度x,收取報頭的bytes格式 header = json.loads(json.dumps(header)) # 提取報頭 # 最後根據報頭的內容提取真實的數據,比如 real_data_len = s.recv(header[‘file_size‘]) s.recv(real_data_len)
# struct的用法
# _*_coding:utf-8_*_ # http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html __author__ = ‘Linhaifeng‘ import struct import binascii import ctypes values1 = (1, ‘abc‘.encode(‘utf-8‘), 2.7) values2 = (‘defg‘.encode(‘utf-8‘), 101) s1 = struct.Struct(‘I3sf‘) s2 = struct.Struct(‘4sI‘) print(s1.size, s2.size) prebuffer = ctypes.create_string_buffer(s1.size + s2.size) print(‘Before : ‘, binascii.hexlify(prebuffer)) # t=binascii.hexlify(‘asdfaf‘.encode(‘utf-8‘)) # print(t) s1.pack_into(prebuffer, 0, *values1) s2.pack_into(prebuffer, s1.size, *values2) print(‘After pack‘, binascii.hexlify(prebuffer)) print(s1.unpack_from(prebuffer, 0)) print(s2.unpack_from(prebuffer, s1.size)) s3 = struct.Struct(‘ii‘) s3.pack_into(prebuffer, 0, 123, 123) print(‘After pack‘, binascii.hexlify(prebuffer)) print(s3.unpack_from(prebuffer, 0))
自定義報頭
# 服務端
import socket, struct, json import subprocess phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加 phone.bind((‘127.0.0.1‘, 8080)) phone.listen(5) while True: conn, addr = phone.accept() while True: cmd = conn.recv(1024) if not cmd: break print(‘cmd: %s‘ % cmd) res = subprocess.Popen(cmd.decode(‘utf-8‘), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() print(err) if err: back_msg = err else: back_msg = res.stdout.read() conn.send(struct.pack(‘i‘, len(back_msg))) # 先發back_msg的長度 conn.sendall(back_msg) # 在發真實的內容 conn.close()
# 客戶端 # _*_coding:utf-8_*_ __author__ = ‘Linhaifeng‘ import socket, time, struct s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) res = s.connect_ex((‘127.0.0.1‘, 8080)) while True: msg = input(‘>>: ‘).strip() if len(msg) == 0: continue if msg == ‘quit‘: break s.send(msg.encode(‘utf-8‘)) l = s.recv(4) x = struct.unpack(‘i‘, l)[0] print(type(x), x) # print(struct.unpack(‘I‘,l)) r_s = 0 data = b‘‘ while r_s < x: r_d = s.recv(1024) data += r_d r_s += len(r_d) # print(data.decode(‘utf-8‘)) print(data.decode(‘gbk‘)) # windows默認gbk編碼
我們可以把報頭做成字典,字典裏包含將要發送的真實數據的詳細信息,然後json序列化,然後用struck將序列化後的數據長度打包成4個字節(4個自己足夠用了)
發送時:
先發報頭長度
再編碼報頭內容然後發送
最後發真實內容
接收時:
先手報頭長度,用struct取出來
根據取出的長度收取報頭內容,然後解碼,反序列化
從反序列化的結果中取出待取數據的詳細信息,然後去取真實的數據內容
#服務端定制復雜的報頭 import socket, struct, json import subprocess phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加 phone.bind((‘127.0.0.1‘, 8080)) phone.listen(5) while True: conn, addr = phone.accept() while True: cmd = conn.recv(1024) if not cmd: break print(‘cmd: %s‘ % cmd) res = subprocess.Popen(cmd.decode(‘utf-8‘), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = res.stderr.read() print(err) if err: back_msg = err else: back_msg = res.stdout.read() headers = {‘data_size‘: len(back_msg)} head_json = json.dumps(headers) head_json_bytes = bytes(head_json, encoding=‘utf-8‘) conn.send(struct.pack(‘i‘, len(head_json_bytes))) # 先發報頭的長度 conn.send(head_json_bytes) # 再發報頭 conn.sendall(back_msg) # 在發真實的內容 conn.close()
# 客戶端 from socket import * import struct, json ip_port = (‘127.0.0.1‘, 8080) client = socket(AF_INET, SOCK_STREAM) client.connect(ip_port) while True: cmd = input(‘>>: ‘) if not cmd: continue client.send(bytes(cmd, encoding=‘utf-8‘)) head = client.recv(4) head_json_len = struct.unpack(‘i‘, head)[0] head_json = json.loads(client.recv(head_json_len).decode(‘utf-8‘)) data_len = head_json[‘data_size‘] recv_size = 0 recv_data = b‘‘ while recv_size < data_len: recv_data += client.recv(1024) recv_size += len(recv_data) print(recv_data.decode(‘utf-8‘)) # print(recv_data.decode(‘gbk‘)) #windows默認gbk編碼
峰哥解決粘包的方式