TCP解决粘包问题,最基础的方法解决TCP粘包,学会用报头包装信息

先传包头的长度,再解包头从包头中取包的长度,来精准解析包内容


<引子> 远程执行ssh命令模块

import subprocess  # 执行远程命令

#  shell=True cmd把字符串转成程序命令
obj = subprocess.Popen('d2ir', shell = True,
                 stdout = subprocess.PIPE,  # 正确管道进入
                 stderr= subprocess.PIPE)  # 错误信息丢入错误管道
print('sudout>:', obj.stdout.read().decode('GBK'))  # Liunx用utf8  windows用gbk!!!  【正确管道】
print('stderr>:', obj.stderr.read().decode('GBK'))  # 【错误管道】

<引子> 利用struct 和 json 模块完成自定义包头传输

import struct  # 把数字打成固定长度, 客户端解析的时候,返回元祖,[0] = 传送信息

res = struct.pack('i',128)  # pac:打包成 byte类型的数据  参数1.格式  i = 短整数  转成4位的byte
res = struct.pack('l',128)  # pac:打包成 byte类型的数据  参数1.格式  l = 长数字  转成8位的byte

<引子> windows ssh默认编码 GBK liunx为 utf-8!!!!

  • 测试命令windows
# dir:查看某一个文件夹下的子文件名与子文件夹名
# tasklist:查看运行的进程
# ipconfig:查看本顶网卡的ip信息

服务端


import socket
import subprocess  # 执行远程命令
import struct  # 包头模块   把数字,转成固定长度byte的模块,用于
import json

obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 回收端口,防止设定端口被占用
obj.bind(('127.0.0.1', 8084))
obj.listen(5)  # 最大监听数
while True:
    conn, ip= obj.accept()  # 等待客户进入
    print('用户接入:',ip)

    while True:
        try:
            ''''【一】 接收命令'''
            cmd = conn.recv(1024)  # 最大字节数1024字节
            if not cmd:break  # 空就跳过(判断断开连接)Liunx系统用
            print('接收到用户指令',cmd)

            ''''【二】执行命令'''
            sub = subprocess.Popen(cmd.decode('GBK'), shell=True,  # windows必须用GBK
                                   stdout=subprocess.PIPE,  # 正确通道
                                   stderr=subprocess.PIPE)  # 错误通道
            stdout = sub.stdout.read()
            stderr = sub.stderr.read()

            ''''【三】把结果返回传给客户端'''
            # 第一步:制作固定长度的包头 字典形式,储存长度、md5 等等
            header_dic = {
                'filename':'测试.txt',
                'md5':'这里可以放md5',
                'total_size': len(stdout)+len(stderr)
            }
            header_json = json.dumps(header_dic)  # 转成字符串
            header_byte = header_json.encode('gbk')  # 转成 byte类型的进行转换,才能进行 send发送  windows 用GBK

            # 第二步:包头长度发送,先把包头长度发过去
            '''如果直接把包头发过去,长度可能会超出struct模块的'i'和'l'模式的长度,所以最稳的方法是,把包头长度先传过去,再传包头,这样就能精准传输'''
            conn.send(struct.pack('i',len(header_byte)))  # 用struct.pack ‘i’ 模式 把数字转成4byte,包头长度先发过去

            # 第三步:包头发送
            conn.send(header_byte)


            # 第四步:发送内容给客户端
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError:  # windows系统 因为要报错,所以捕捉异常
            print('该客户断开已断开连接')
            break
    conn.close()
    print('关闭管道')
obj.close()

客户端

import json
import socket
import struct

obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
obj.connect(('127.0.0.1', 8084))

while True:

    '''【一】发命令'''
    cmd = input('请输入指令>>>').strip()
    if not cmd:
        continue  # 如果输入的为空,直接跳过
    obj.send(cmd.encode('GBK'))  # windows必须用GBK

    '''【二】接收数据  解决粘包问题'''
    # 第一步:取包头长度
    header_size = struct.unpack('i', obj.recv(4))[0]  # 服务端传过来是4byte的,所以反解出,取元祖第一个值,为包头长度

    # 第二步:通过包头长度取包头.此时为byte类型
    header_bytes = obj.recv(header_size)

    # 第三步:从包头中皆系出真实的描述信息包头 字典
    header_json = header_bytes.decode('gbk')  # 把 byte格式转成str
    header_dic = json.loads(header_json)  # 再把str 转成对应的格式 dict
    total_size = header_dic.get('total_size')  # 取出信息长度
    print('包头信息', header_dic)

    # 第四步:去掉包头后的真实数据
    recv_size = 0
    recv_data = b''
    while recv_size < total_size:
        res = obj.recv(1024)
        recv_data += res
        recv_size += len(res)
    print(recv_data.decode('GBK')) # windows必须用GBK

obj.close()

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值