9

tcp协议传输中的粘包问题 - Maxs_hu

 2 years ago
source link: https://www.cnblogs.com/Maxs-message/p/16535788.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

什么是粘包问题

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



About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK