• 黏包及解决方法


    !!!!!!只有TCP有粘包现象,UDP永远不会粘包

    什么是黏包?

      接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

    黏包问题的产生原因:

          接收方 不知道对方发了多少数据
          而TCP 会把所有收到的数据 拼接到一起 放到系统缓存中
          UDP 不会黏包 因其实基于数据包发送数据
    黏包问题的解决方法:

       发送方 先发送数据长度  接收方先接收长度  长度必须固定字节
          自定义报头  不仅可以传输 数据长度 还能添加任意额外信息(报头格式建议使用json 可以跨平台)

     1 import  socket
     2 import subprocess
     3 import struct
     4 server = socket.socket()
     5 server.bind(("127.0.0.1",9090))
     6 server.listen()
     7 
     8 while True:
     9     client,addr = server.accept()
    10     while True:
    11         try:
    12             # 接收命令
    13             cmd = client.recv(1024).decode("utf-8")
    14             p = subprocess.Popen(cmd,shell=True,stdout=-1,stderr=-1)
    15             # data与err_data 都是采用的系统编码 windows是GBK
    16             data = p.stdout.read()
    17             err_data = p.stderr.read()
    18 
    19             print("数据长度:%s" % (len(data)   + len(err_data)))
    20 
    21             # 先发送长度给客户端
    22             length = len(data)   + len(err_data)
    23 
    24             len_data = struct.pack("i",length)
    25             # 先发送长度 在发真实数据 有可能 长度数据和真实数据黏在一起 而接收方不知道长度数据的字节数 导致黏包
    26             # 解决的方案就是 长度信息占的字节数固定死  整数 转成一个固定长度字节
    27 
    28             client.send(len_data)
    29 
    30             client.send(data)
    31             client.send(err_data)
    32         except ConnectionResetError:
    33             client.close()
    34             print("连接中断......")
    35             break
    黏包问题的基础解决方案(服务端)
     1 import socket
     2 import struct
     3 
     4 c = socket.socket()
     5 c.connect(("127.0.0.1",9090))
     6 while True:
     7     cmd = input(">>:").strip()
     8 
     9     c.send(cmd.encode("utf-8"))
    10 
    11     # 先接收长度  长度固定为4个字节
    12     length = c.recv(4)
    13     len_data = struct.unpack("i",length)[0] # 转换为整型
    14     print("数据长度为%s" % len_data)
    15 
    16     all_data = b"" # 存储已接收数据
    17     rcv_size = 0 # 已接收长度
    18     # 接收真实数据
    19     # 循环接收 直到 接收到的长度等于总长度
    20     while  rcv_size < len_data:
    21         data = c.recv(1024)
    22         rcv_size += len(data)
    23         all_data += data
    24 
    25     print("接收长度%s" % rcv_size)
    26     print(all_data.decode("gbk"))
    黏包问题的基础解决方案(客户端)

    struct 模块的使用

      struct 可以将python中的数据类型转换为bytes类型

     1 num = 1024
     2 
     3 import struct
     4 
     5 # 该函数 将一个python的数据转成bytes   
     6 # 第一个参数通常是i 其能转换的数据范围是c语言的int范围
     7 # 如果int不够 那就用q  表示long long 型(具体可参照官方文档,一般情况下i已经足够使用)
     8 res = struct.pack("i",num)
     9 print(res)
    10 print(len(res))
    struct数据转换
    1 num = 1024
    2 
    3 import struct
    4 
    5 res = struct.pack("i",num)
    6 # 从字节转回整型
    7 res2 = struct.unpack("i",res)
    8 print(res2[0])
    struct数据类型回转

    黏包问题高级解决方案

     1 import socket
     2 import subprocess
     3 import datetime
     4 import json
     5 import struct
     6 
     7 # 创建对象
     8 soc = socket.socket()
     9 
    10 # 绑定ip 和 端口
    11 soc.bind(('127.0.0.1', 8888))
    12 
    13 # 监听
    14 soc.listen()
    15 
    16 while True:
    17     client, addr = soc.accept()
    18     while True:
    19         try:
    20             # 接收命令
    21             cmd = client.recv(1024).decode('utf-8')
    22             p = subprocess.Popen(
    23                                 cmd,
    24                                 shell=True,
    25                                 stdout=subprocess.PIPE,
    26                                 stderr=subprocess.PIPE
    27             )
    28             # data 与err_data 都是采用系统编码 Windows 采用'GBK'
    29             data = p.stdout.read()
    30             err_data = p.stderr.read()
    31 
    32             # 计算真实数据长度
    33             length = len(data) + len(err_data)
    34 
    35             t = {}
    36             t['time'] = str(datetime.datetime.now())
    37             t['size'] = length
    38 
    39             # 得到json格式字符串
    40             t_json = json.dumps(t)
    41             # 将json转换为字节
    42             t_data = t_json.encode('utf-8')
    43             t_length = struct.pack('i', len(t_data))
    44 
    45             # 发送额外信息长度
    46             client.send(t_length)
    47             # 发送额外信息
    48             client.send(t_data)
    49 
    50             # 发送真实数据
    51             client.send(data)
    52             client.send(err_data)
    53 
    54         except ConnectionRefusedError:
    55             client.close()
    56             print('链接中断')
    57             break
    服务端
     1 import socket
     2 import struct
     3 import json
     4 
     5 client = socket.socket()
     6 
     7 client.connect(('127.0.0.1', 8888))
     8 
     9 while True:
    10     cmd = input('>>>:').strip().encode('utf-8')
    11     if not cmd:
    12         print('cmd命令不能为空')
    13         continue
    14     client.send(cmd)
    15 
    16     # 接受额外信息长度
    17     length = client.recv(4)
    18     len_data = struct.unpack("i", length)[0]
    19     #接受额外信息
    20     t_json = client.recv(len_data)
    21     json_dic = json.loads(t_json.decode('utf-8'))
    22     print('执行时间为%s'%json_dic['time'])
    23     data_size = json_dic['size']
    24 
    25     #接收真实数据
    26     all_data = b''
    27     rcv_size = 0
    28 
    29     while rcv_size < data_size:
    30         data = client.recv(1024)
    31         rcv_size += len(data)
    32         all_data += data
    33 
    34     print('接收长度为%s' % rcv_size)
    35     print(all_data.decode('gbk'))
    客户端
    学习,学习,学习! 学习是为了更好的未来,不要让别人瞧不起你,加油!!!
  • 相关阅读:
    排序算法
    顺序表与链表
    二叉树
    查找算法
    15 Django 离线脚本
    poj 1330 Nearest Common Ancestors(LCA 基于二分搜索+st&rmq的LCA)
    hdu 6158 The Designer( 反演圆)
    圆的反演性质
    HDU 6153 A Secret(扩展kmp)
    kmp&扩展kmp
  • 原文地址:https://www.cnblogs.com/yangyufeng/p/10179940.html
Copyright © 2020-2023  润新知