zoukankan      html  css  js  c++  java
  • python中网络编程中的黏包现象

    黏包中的关键因素 : 缓冲区

    缓冲区 :  将程序和网络解耦

    缓冲区大致有输入缓冲区 和 输出缓冲区两种形式,这也是产生黏包的因素.大致的缓冲情况 如下图所示:

    每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
    
    write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
    
    TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。
    
    read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
    
    这些I/O缓冲区特性可整理如下:
    
    1.I/O缓冲区在每个TCP套接字中单独存在;
    2.I/O缓冲区在创建套接字时自动生成;
    3.即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
    4.关闭套接字将丢失输入缓冲区中的数据。
    
    输入输出缓冲区的默认大小一般都是 8K,可以通过 getsockopt() 函数获取:
    
    1.unsigned optVal;
    2.int optLen = sizeof(int);
    3.getsockopt(servSock, SOL_SOCKET, SO_SNDBUF,(char*)&optVal, &optLen);
    4.printf("Buffer length: %d
    ", optVal);

    两种黏包现象:
    1.连续的小包可能会被优化算法给组合到一起进行发送
    2.第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱
    具体情况如图所示:


    模拟黏包现象
     1 #黏包现象服务端:
     2 import socket
     3 import subprocess
     4 server = socket.socket()
     5 ip_port = ('127.0.0.1',8001)
     6 
     7 server.bind(ip_port)
     8 
     9 server.listen()
    10 
    11 conn,addr = server.accept()
    12 
    13 while 1:
    14     from_client_cmd = conn.recv(1024)
    15 
    16     print(from_client_cmd.decode('utf-8'))
    17 
    18     sub_obj = subprocess.Popen(
    19         from_client_cmd.decode('utf-8'),
    20         shell=True,  #固定
    21         stdout=subprocess.PIPE,  #标准输出 PIPE:管道,保存着指令单额执行结果
    22         stderr=subprocess.PIPE   #标准错误输出
    23     )
    24 
    25     std_msg = sub_obj.stdout.read()
    26     print('指令的执行结果长度>>>>',len(std_msg))
    27 
    28     conn.send(std_msg)
    #黏包客户端:
    import socket
    
    client = socket.socket()
    client.connect(('127.0.0.1',8001))
    
    while 1:
        cmd = input('请输入指令:')
    
        client.send(cmd.encode('utf-8'))
    
        server_cmd_result = client.recv(1024)
    
        print(server_cmd_result.decode('gbk'))
    黏包解决方案
    黏包解决大致思路:问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,
    如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端发一个确认消息给发送端,
    然后发送端再发送过来后面的真实内容,接收端再来一个死循环接收完所有数据。
    
    
     1 # 黏包解决服务端:
     2 import socket
     3 import subprocess
     4 import struct
     5 server = socket.socket()
     6 ip_port = ('127.0.0.1',8001)
     7 
     8 server.bind(ip_port)
     9 
    10 server.listen()
    11 
    12 conn,addr = server.accept()
    13 
    14 while 1:
    15     from_client_cmd = conn.recv(1024)
    16 
    17     print(from_client_cmd.decode('utf-8'))
    18     #接收到客户端发送来的系统指令,服务端通过subprocess模块到服务端自己的系统里面执行这条指令
    19     sub_obj = subprocess.Popen(
    20         from_client_cmd.decode('utf-8'),
    21         shell=True,
    22         stdout=subprocess.PIPE,  #正确结果的存放位置
    23         stderr=subprocess.PIPE   #错误结果的存放位置
    24     )
    25     #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果
    26     std_msg = sub_obj.stdout.read()
    27 
    28     #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据
    29     std_msg_len = len(std_msg)
    30 
    31     print('指令的执行结果长度>>>>',len(std_msg))
    32 
    33     msg_lenint_struct = struct.pack('i',std_msg_len)
    34 
    35     conn.send(msg_lenint_struct+std_msg)
    
    
     1 #黏包解决方案客户端:
     2 import socket
     3 import struct
     4 client = socket.socket()
     5 client.connect(('127.0.0.1',8001))
     6 
     7 while 1:
     8     cmd = input('请输入指令:')
     9     #发送指令
    10     client.send(cmd.encode('utf-8'))
    11 
    12     #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度
    13     server_res_len = client.recv(4)
    14     msg_len = struct.unpack('i',server_res_len)[0]
    15 
    16     print('来自服务端的消息长度',msg_len)
    17     #通过解包出来的长度,来接收后面的真实数据
    18     server_cmd_result = client.recv(msg_len)
    19 
    20     print(server_cmd_result.decode('gbk'))

     今天的分享就到此暂告一个段落,有兴趣的小伙伴多多交流,有错误不足之处请提出见解,一起学习纠错

     
  • 相关阅读:
    初探nodejs事件循环机制event loop
    夯实基础之--new关键字、instanceOf原理
    分享-结合demo讲解JS引擎工作原理
    Linux-centos安装node、nginx小记
    openlayers5实战--踩坑总结
    node+koa中转层开发实践总结
    vue预渲染实践总结
    css多行省略-webkit-box-orient打包编译后失效原因
    使用mpVue开发小程序实战总结
    Linux crontab定时执行任务
  • 原文地址:https://www.cnblogs.com/Godisgirl/p/10222866.html
Copyright © 2011-2022 走看看