• day30 粘包


    一,什么是粘包?

    发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。

    所谓粘包问题,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成。

    两种情况下会发生粘包:

    一种:

    发送端需要等缓冲区满了才发送出去,造成粘包(发送数据时间间隔很短,数据很小,就会合到一起,产生粘包)

    二种:

    接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包。)

    解决粘包的方法:

    为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端再接收时,先从缓存中取出固定长的报头,然后再取真实数据。

    补充知识:

    struct模块

    该模块可以吧一个整形,转成固定长度的bytes类型。

    import struct
    s=struct.pack('i',123232)
    print(s,len(s))
    s1=struct.unpack('i',s)
    print(s1)


    我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)

    发送时:

    先发报头长度

    再编码报头内容然后发送

    最后发真实内容

    接收时:

    先手报头长度,用struct取出来

    根据取出的长度收取报头内容,然后解码,反序列化

    从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

    服务端:

    from socket import *
    import subprocess
    import struct
    import json

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

    while True:
    conn,client_addr=server.accept()
    print('新的客户端',client_addr)

    while True:
    try:
    cmd=conn.recv(1024) #cmd=b'dir'
    if len(cmd) == 0:break

    # 运行系统命令
    obj=subprocess.Popen(cmd.decode('utf-8'),
    shell=True,
    stderr=subprocess.PIPE,
    stdout=subprocess.PIPE
    )

    stdout=obj.stdout.read()
    stderr=obj.stderr.read()

    #先制作报头
    header_dic={
    'filename':'a.txt',
    'total_size':len(stdout) + len(stderr),
    'hash':'xasf123213123'
    }
    header_json=json.dumps(header_dic)
    header_bytes=header_json.encode('utf-8')

    #1、先把报头的长度len(header_bytes)打包成4个bytes,然后发送
    conn.send(struct.pack('i',len(header_bytes)))
    #2、发送报头
    conn.send(header_bytes)
    #3、再发送真实的数据
    conn.send(stdout)
    conn.send(stderr)
    except ConnectionResetError:
    break

    conn.close()


    客户端:

    from socket import *
    import struct
    import json

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

    while True:
    cmd=input('>>: ').strip()
    if len(cmd) == 0:continue
    client.send(cmd.encode('utf-8'))

    #1、先收4个字节,该4个字节中包含报头的长度
    header_len=struct.unpack('i',client.recv(4))[0]

    #2、再接收报头
    header_bytes=client.recv(header_len)

    #从报头中解析出想要的内容
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    print(header_dic)
    total_size=header_dic['total_size']

    #3、再收真实的数据
    recv_size=0
    res=b''
    while recv_size < total_size :
    data=client.recv(1024)
    res+=data
    recv_size+=len(data)

    print(res.decode('gbk'))



  • 相关阅读:
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(45)-工作流设计-设计步骤
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(44)-工作流设计-设计表单
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(43)-工作流设计-字段分类设计
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(42)-工作流设计-表建立
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(41)-组织架构
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(40)-精准在线人数统计实现-【过滤器+Cache】
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(39)-在线人数统计探讨
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(38)-Easyui-accordion+tree漂亮的菜单导航
    ASP.NET MVC5+EF6+EasyUI 后台管理系统(37)-文章发布系统④-百万级数据和千万级数据简单测试
    .Net 转战 Android 4.4 日常笔记(10)--PullToRefresh下拉刷新使用
  • 原文地址:https://www.cnblogs.com/fxc-520520/p/9285776.html
Copyright © 2020-2023  润新知