1. 程式人生 > 實用技巧 >soket粘包問題及解決方案

soket粘包問題及解決方案

一、粘包問題

問題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'\x10\x00\x00\x00'
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()