tcp協議傳輸中的粘包問題

什麼是粘包問題

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

  而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()