tcp協定傳輸中的粘包問題

2022-07-31 06:03:09

什麼是粘包問題

  tcp是流體協定. 其nagle演演算法會將資料量較小. 並且傳送間隔時間較短的多個封包合併為一個傳送. 網路傳輸的時候是一段一段位元組流的傳送. 在接收方看來根本不知道位元組流從何開始. 在哪裡結束. 所以粘包問題就是接收方不知道訊息之間的界限. 不止到一次性提取多少資料導致的

  而udp協定的是訊息導向(資料包)的協定. 每一段的udp都是一段訊息. 應用程式必須以訊息作為單位提取. 不能提取任意自己的資料. 而且udp協定並不建立連線. 只管傳送不管對方是否收到. 所以不存在粘包問題

怎麼解決粘包問題

​ 設定一個固定的報頭. 報頭中含有真實資料的長度資訊. 然後使用者端就可以根據報頭的資料去接收相應位元組. 從而避免粘包現象. 總結起來就是一開始將真實資料長度通過報頭傳遞給使用者端. 後面都是環環相扣

具體程式碼

​ 前面都是理論部分. 後面咱們來看看怎麼進行實操解決粘包問題. 解決粘包問題的關鍵就是讓使用者端知道資料之間的界限在哪.

# 伺服器端.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/30 13:07
# @author: Maxs_hu
"""
以前有種比較low的方式(alex)是使用time.sleep將資料流之間斷開. 當然這種自己設定網路延遲的方式當然是不可取的
"""

from socket import *
import subprocess
import json
import struct


socket = socket(AF_INET, SOCK_STREAM)  
socket.bind(('127.0.0.1', 8000))
socket.listen(5)

while True:  # 連結迴圈
    print('---伺服器開始執行---')
    conn, client_addr = socket.accept()
    print(client_addr)

    while True:
        try:
            cmd = conn.recv(1024)
            if len(cmd) == 0:
                break
            obj = subprocess.run(cmd.decode('utf8'),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE,
                                 encoding='gbk'
                                 )
            stdout = obj.stdout.encode('utf8')
            stderr = obj.stderr.encode('utf8')
            data_size = len(stderr+stdout)
            # 1. 製作合理的表頭資料
            header_dic = {
                'filename': 'a.txt',
                'total_size': data_size,
                'hashlib': 'fdfadfadf343jkafjdxkfjc'
            }
            # 將字典轉化成可以傳輸的格式. 並計算出len
            header_json = json.dumps(header_dic)
            header_byte = header_json.encode('utf8')
            header_len = struct.pack('i', len(header_byte))

            # 1. 先將表頭長度進行傳遞
            conn.send(header_len)

            # 2. 再將表頭資料進行傳輸
            conn.send(header_byte)

            # 3. 在傳輸真實的資料
            conn.send(stderr+stdout)
        except ConnectionResetError:
            break

    conn.close()

# 使用者端.py

# -*- encoding:utf-8 -*-
# @time: 2022/7/30 13:07
# @author: Maxs_hu
from socket import *
import struct
import json


client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8000))

while True:
    cmd = input('請輸入命令>>>').strip()
    if len(cmd) == 0:
        break
    client.send(cmd.encode("utf8"))
    # 1. 先接收表頭長度
    header_len = client.recv(4)
    header_size = struct.unpack('i', header_len)[0]

    # 2. 根據表頭的長度去接收表頭
    header = client.recv(header_size)

    # 解析表頭資料
    header_dic = json.loads(header.decode('utf8'))
    print(header_dic)
    total_size = header_dic['total_size']

    recv_size = 0
    data = b''
    while recv_size < total_size:
        data += client.recv(1024)
        recv_size = len(data)
    print(data.decode('utf8'))
client.close()