zoukankan      html  css  js  c++  java
  • 模拟ssh的远程网络传输

    粘包产生的原因分析:

    第一点:客户端向服务端发起命令请求,服务端接受命令请求,并返回对应的信息,如果信息过大,客户端一次接受不了,那么下一次请求依然返回

    上一个命令的内容,就出现了粘包的情况。

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

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

    发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做,虽然节省了时间,但是发出的包却粘在了一起,造成粘包现象。

    简单点的报头(有注释)

    from socket import *  # socket里面很多功能用得到,属于特殊情况
    import subprocess
    import struct
    
    server=socket(AF_INET,SOCK_STREAM)  # 生成套接字,绑定ip_port,服务端处于倾听状态
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    while True:
        conn,client_addr=server.accept() # 套接字和绑定的ip_port
        print('新的客户端',client_addr)
    
        while True:  # 下面的不循环结束不会进入下一个客户端
            try:
                cmd=conn.recv(1024) # cmd=b'dir'  # 命令一般不会超过1024 不用管
                if len(cmd) == 0:break
    
                # 运行系统命令 为什么不用system 因为system???
                obj=subprocess.Popen(cmd.decode('utf-8'),  # 字节解压成字符串
                                 shell=True,
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE
                                 )
    
                stdout=obj.stdout.read()# 读出管道里数据 顺序不要乱 先stdout 然后stderr
                stderr=obj.stderr.read()
    
                #1、先制作报头(固定长度)
                total_size=len(stdout) + len(stderr)  # 数据的总长度(整型)
                header=struct.pack('i',total_size) # 把数据的长度按照i格式包装为固定的四个字节的报头
    
                #2、先发送固定长度的报头
                conn.send(header) # 为什么能解决粘包问题?因为把报头固定为四个字节,接受的时候固定接受
    
                #3、再发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
    
        conn.close()
    服务端
    from socket import *
    import struct
    
    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、先收固定长度的报头
        header=client.recv(4)  # 先接收固定长度的报头
    
        #2、从报头中解析出对数据的描述信息
        total_size=struct.unpack('i',header)[0] # 按照i格式解析出报头的数据信息,解析出来是一个元组(a,)形式,取第一个
    
        #3、再收真实的数据
        recv_size=0 # 应用层接受数据,初始数据为0
        res=b''
        while recv_size < total_size : # 接受数据小于传送过来的数据时,循环会一直进行下去
            data=client.recv(1024)
            res+=data
            recv_size+=len(data)
    
        print(res.decode('gbk'))
    客户端

    复杂一点的报头(有注释)

    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={ # 报头信息 包含文件名  文件大小 hash值
                    'filename':'a.txt',
                    'total_size':len(stdout) + len(stderr),
                    'hash':'xasf123213123'
                }
                header_json=json.dumps(header_dic) # 将包含报头信息的字典存成字符串格式
                header_bytes=header_json.encode('utf-8') # 字符串编码成字节形式
                print(header_json,type(header_json))
    
                #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]
        print(client.recv(4))
        print(header_len)
    
        #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'))
    客户端
  • 相关阅读:
    ZOJ4134 Unrooted Trie(dfs序+线段树)
    ZOJ4127 Grid with Arrows(欧拉路径的判断)
    CF1037E Trips(思维)
    django学习第十四天--Forms和ModelForm
    django学习第十三天--自定义中间件
    图书管理系统---基于ajax删除数据
    django学习第十二天--ajax请求和csrftoken认证的三种方式
    django中修改QueryDict数据类型和转成普通字典
    图书管理系统进阶---多表操作
    locals()用法
  • 原文地址:https://www.cnblogs.com/Roc-Atlantis/p/9285757.html
Copyright © 2011-2022 走看看