zoukankan      html  css  js  c++  java
  • python 黏包

    黏包

    黏包的起因:

    连续send多个小数据,会发生黏包现象,这个是tcp协议优化算法造成的

    当发送一个数据超过本次接收的最大范围之后,剩下的数据会留到下次接收时接收

    黏包的现象:

    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8090))
    sk.listen()
    conn,addr = sk.accept()
    ret = conn.recv(2)   #在这里先接收一个两字节的内容
    ret2 = conn.recv(10) #然后在这里在接收一个长度为10字节的内容
    print(ret)
    print(ret2)
    conn.close()
    sk.close()
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8090))
    sk.send(b'helloeve')  #在这里发送一个长度为8的字节
    sk.close()

    b'he'
    b'lloeve'

    可以看见接收的内容被分开了

    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8090))
    sk.listen()
    conn,addr = sk.accept()
    ret1 = conn.recv(12)  #在这里接收两次
    print(ret1)
    ret2 = conn.recv(12)
    print(ret2)
    conn.close()
    sk.close()
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8090))
    sk.send(b'hello')  #这里连续发送两次数据
    sk.send(b'eve')
    sk.close()

    b'helloeve'
    b''

    原本预计的接收两个内容被合成一个接收了

    这就是黏包

    所以,黏包的根本原因就是,接收方不知道本次接收的数据具体大小,才会发生黏包

    如何解决黏包

    竟然知道是什么原因才发生黏包,那么,只要每次发送数据前,告诉对方我要发多少数据不就行了

    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    conn,addr = sk.accept()
    while True:
        cmd = input('>>>')
        if cmd == 'q':
            conn.send(b'q')
            break
        conn.send(cmd.encode('gbk'))
        num = conn.recv(1024).decode('utf-8')  #将接到的长度赋给一个变量
        conn.send(b'ok')                        #想用户端发送消息表示已经接收到长度
        res = conn.recv(int(num)).decode('gbk')  # 将接收到的长度值给接收字节数
        print(res)
    conn.close()
    sk.close()
    import socket
    import subprocess      #调用subprocess模块
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    while True:
        cmd = sk.recv(1024).decode('gbk')
        if cmd == 'q':
            break
        res = subprocess.Popen(cmd,shell=True,
                         stdout=subprocess.PIPE,    #将取到的东西放到管道里
                         stderr=subprocess.PIPE)    # PIPE 管道
        std_out = res.stdout.read()       #将管道里的数据赋给一个变量
        std_err = res.stderr.read()       #因为管道里的东西只能取一次
        sk.send(str(len(std_out)+len(std_err)).encode('utf-8'))   #叫测量的长度发给服务端
        sk.recv(1024)       #接收服务端的反馈
        sk.send(std_out)    # 将数据发过去
        sk.send(std_err)
    sk.close()

    这样每次发送前都将本次要发送的数据字节数发过去,让对方对应接收就不会发生黏包现象了

    不过,虽然不会黏包,但是每次发送都需要一次交互,增加了代码的工作

    怎么才能一次交互就解决这些问题呢

    这时候就要用到

    struct模块

    struct模块可以把一个类型转成固定长度的bytes

    我们这次只需要用到int

    import struct
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    conn,addr = sk.accept()
    while True:
        cmd = input('>>>')
        if cmd == 'q':
            conn.send(b'q')
            break
        conn.send(cmd.encode('gbk'))
        num = conn.recv(4)               #接受客户端的传来的
        num = struct.unpack('i',num)[0]  #反向得到字节数
        res = conn.recv(int(num)).decode('gbk')  #将字节数传给接收端当参数
        print(res)               
    conn.close()
    sk.close()
    import struct    #这时候就需要用到struct模块
    import socket
    import subprocess
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    while True:
        cmd = sk.recv(1024).decode('gbk')
        if cmd == 'q':
            break
        res = subprocess.Popen(cmd,shell=True,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
        std_out = res.stdout.read()
        std_err = res.stderr.read()
        len_num = len(std_out)+len(std_err)   #将计算的字节数赋给一个变量
        num_by = struct.pack('i',len_num)     #在将字节数用strect模块转成长度为4的bytes
        sk.send(num_by)                       #然后将它发给服务端
        sk.send(std_out)
        sk.send(std_err)
    sk.close()
    在网络上传输的所有数据 都叫数据包
    数据包里的所有数据 都叫报文
    报文里不止有你的数据 还有 ip地址 mac地址 端口号

    所有的报文 都有 报头
  • 相关阅读:
    菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t[转]
    菜鸟nginx源码剖析数据结构篇(十) 自旋锁ngx_spinlock[转]
    菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t[转]
    菜鸟nginx源码剖析数据结构篇(八) 缓冲区链表ngx_chain_t[转]
    菜鸟nginx源码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)[转]
    菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)[转]
    菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t[转]
    菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]
    菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t[转]
    菜鸟nginx源码剖析数据结构篇(二) 双向链表ngx_queue_t[转]
  • 原文地址:https://www.cnblogs.com/GrandDarkness/p/8379296.html
Copyright © 2011-2022 走看看