zoukankan      html  css  js  c++  java
  • 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'))



  • 相关阅读:
    java.lang.NoSuchMethodError:antlr.collections.AST.getLine() I
    T7 java Web day01 标签HTML
    T6 s1 day19
    T5 s5 Day18
    T5 s4 Day 17
    T5 s3 day16
    T5 s2 Day 15
    T5 s1 day14
    T4 S03 day 12
    T4 S01 day1
  • 原文地址:https://www.cnblogs.com/fxc-520520/p/9285776.html
Copyright © 2011-2022 走看看