1. 程式人生 > >峰哥解決粘包的方式

峰哥解決粘包的方式

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編碼

峰哥解決粘包的方式