• 网络编程之模拟ssh远程执行命令、粘包问题 、解决粘包问题


    模拟ssh远程执行命令

    服务端

    import socket
    import subprocess
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    server.bind(('127.0.0.1', 8000))
    
    server.listen(5)
    
    print('strat...')
    while True:
        
        conn, client_addr = server.accept() # 可以建立多个来连接
        
        while True:
            cmd = conn.recv(1024)
            
            pipeline = subprocess.Popen(cmd.decode('utf-8'),   # 输入的cmd命令
                                       shell=True,  # 通过shell运行
                                       stderr=subprocess.PIPE,   # 把错误输出放入管道,以便打印
                                       stdout=subprocess.PIPE)   # 把正确输出放入管道,以便打印
            
            stdout = pipeline.stdout.read()
            stderr = pipeline.stderr.read()
            
            conn.send(stdout + stderr)
            
        conn.close()
    server,close()
    

    客户端

    from socket import *
    
    client = socket(AF_INET, SOCK_STREAM)
    
    client.connect(('127.0.0.1', 8000))
    
    while True:
        data = input('请输入命令>>>')
        
        client.send(data.encode('utf-8'))
        
        msg = client.recv(1024)
        
        print(msg.decode('gbk'))   # 与操作系统在交互,解码使用gbk解码
        
    client.close()
    

    粘包问题

    什么是粘包

    注意:只有TCP协议会有粘包问题,UDP协议不会粘包

    • socket收发消息原理

    因为服务端一次性把数据给出来了,但是客户端每次只能接受1024个字节的数据,如果数据量较大,服务端就需要一个缓存来存放数据,然后等待客户端一段一段的接收数据;

    但是如果此时客户端又发出一个需求,服务端接收到需求,把数据再次丢入缓存,此时服务端里面的缓存里还有数据,则就会直接接在第一次数据后面,等客户端接受时,并不会分辨第一次和第二次的数据,直接又拿1024个字节,这样就会发生第一次数据后面又第二次请求的数据,这就是粘包。

    • 所谓的粘包问题主要就是因为接收端不知道消息的界限,不知道一次性取多少个字节的数据造成的。

    TCP发送数据的四种情况

    假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下4种情况。

    1. 服务端分两次读取到了两个独立的数据包,分别是D1和D2,没有粘包和拆包;
    2. 服务端一次接收到了两个数据包,D1和D2粘合在一起,被称为TCP粘包;
    3. 服务端分两次读取到了两个数据包,第一次读取到了完整的D1包和D2包的部分内容,第二次读取到了D2包的剩余内容,这被称为TCP拆包;
    4. 服务端分两次读取到了两个数据包,第一次读取到了D1包的部分内容D1_1,第二次读取到了D1包的剩余内容D1_2和D2包的整包。

    粘包的两种情况

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

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

    解决粘包问题

    struct模块

    struct 模块可以把数据型打包成一个4-8 bytes的数据

    import struct
    
    data = struct.pack('i', 24789)  # 压缩
    
    print(data)
    print(len(data))
    
    b'xd5`x00x00'
    4
    
    import struct
    
    data = struct.pack('i', 24789)
    
    res = struct.unpack('i', data)
    print(res[0])
    
    24789
    

    解决粘包问题

    解决粘包问题的核心就是:为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。

    服务端

    import socket
    import struct
    import subprocess
    
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    server.bind(('127.0.0.1', 8000))
    
    server.listen(5)
    
    print('strat...')
    while True:
        
        conn, client_addr = server.accept() # 可以建立多个来连接
        
        while True:
            cmd = conn.recv(1024)
            
            pipeline = subprocess.Popen(cmd.decode('utf-8'),   # 输入的cmd命令
                                       shell=True,  # 通过shell运行
                                       stderr=subprocess.PIPE,   # 把错误输出放入管道,以便打印
                                       stdout=subprocess.PIPE)   # 把正确输出放入管道,以便打印
            
            stdout = pipeline.stdout.read()
            stderr = pipeline.stderr.read()
            
            length = len(stdout) + len(stderr)
            
            # 1、先把报头的长度len(header_bytes)打包成4个bytes,然后发送
            conn.send(struct.pack('i', length))
            # 2、发送真实数据
            conn.send(stdout + stderr)
            
        conn.close()
    server,close()
    

    客户端

    import struct
    from socket import *
    
    client = socket(AF_INET, SOCK_STREAM)
    
    client.connect(('127.0.0.1', 8000))
    
    while True:
        data = input('请输入命令>>>')
        
        client.send(data.encode('utf-8'))
        
        res = client.recv(4)
        
        msg = client.recv(struct.unpack('i', res)[0])
        
        print(msg.decode('gbk'))   # 与操作系统在交互,解码使用gbk解码
        
    client.close()
    
  • 相关阅读:
    <a>元素生成多个<a>的问题,元素标签结尾影响
    mysql不能插入中文数据
    java: Multiple encodings set for module chunk test "GBK" will be used by compiler
    过滤器第二篇【高级应用】
    过滤器第一篇【介绍、入门、简单应用】
    客户关系管理系统
    Mysql中文乱码
    用户登陆注册【JDBC版】
    Mysql的基本命令图
    购物车小案例【简单版】
  • 原文地址:https://www.cnblogs.com/Hades123/p/11099741.html
Copyright © 2020-2023  润新知