zoukankan      html  css  js  c++  java
  • 20 Apr 18 粘包问题及解决方法

    20 Apr 18
    一、上节课复习
    1、 TCP(建立的是一个双向连接)三次握手建连接,四次挥手断连接
    三次握手:
       c----syn=1 seq=x--->s
       s----ack=1+x syn=1 seq=y--->c
       c----ack=1+y------->s
       四次挥手:
       s------fin=1---------->c
       c------>ack=1--------->s
       c------>fin=1--------->s
       s------>ack=1--------->c
       
    127.0.0.1: 本地回环地址
    无论收发(send,receive)都是和自己的操作系统直接交互,由操作系统接着调硬件将信息传到对方的操作系统。
    也是基于以上原因,收发不是一一对应的。
     
    2、socket
       socket是位于应用层与传输层之间的一个抽象层,专门把传输层以下的协议封装成接口提供给应用层使用。应用只需要调用socket的接口或者说按照socket的标准编写程序,写出的程序自然是遵循tcp/ip协议。
     
    二、粘包问题
    TCP又称流式协议,有两个特性:
    a、 像流水一样源源不断
    b、 优化传输机制,用NAGLE算法,将时间间隔短的数据量少的数据攒成一波
    以上两个特性导致了粘包问题
     
    解决粘包问题的思路是知道发多少,然后收多少
    注:如果用time.sleep解决,把这一端解决了,有可能在另一端发生粘包
     
    三、解决粘包问题(基本板)
    a、 struct模块
    1)把整型数字转成bytes类型
    2)转成的bytes是固定长度的
     
    import struct
    res=struct.pack('i',20332)   # ‘i’ --integer
    print(res,len(res)) 
    res2=struct.unpack('i',res)
    print(res2[0])
     
    b、 服务端
    from socket import *
    import subprocess
    import struct
     
    server=socket(AF_INET,SOCK_STREAM)
    server.bind(('127.0.0.1',8080))
    server.listen(5)
     
    while True:
        conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
        print(client_addr)
        while True:
            try:
                cmd=conn.recv(1024)
                obj=subprocess.Popen(cmd.decode('utf-8'),
                                     shell=True,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE
                                     )
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
     
                # 1、制作固定长度的报头
                total_size=len(stdout) + len(stderr)
                header=struct.pack('i',total_size)
     
                # 2、发送报头
                conn.send(header)
     
                #3、发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
     
        conn.close()
    server.close()
     
    c、 客服端
    from socket import *
    import struct
     
    client=socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    # print(client)
     
    while True:
        cmd=input('>>>: ').strip()
        if not cmd:continue
        client.send(cmd.encode('utf-8'))
        #1、先收固定长度的报头
        header=client.recv(4)
        #2、解析报头
        total_size=struct.unpack('i',header)[0]
        print(total_size) #1025
        #3、根据报头内的信息,收取真实的数据
     
        recv_size=0
        res=b''
        while recv_size < total_size:
            recv_data=client.recv(1024)
            res+=recv_data
            recv_size+=len(recv_data)
     
        print(res.decode('gbk'))
    client.close()
     
    四、解决粘包问题(终极版)
    a、 struct模块
    import struct
    import json
     
    header_dic = {
        'total_size': 3122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222121,
        'md5': '123svsaef123sdfasdf',
        'filename': 'a.txt'
    }
     
    header_json=json.dumps(header_dic)
    # print(header_json,type(header_json))
    header_bytes=header_json.encode('utf-8')
    header_size=len(header_bytes)
     
    print(header_size)
    obj=struct.pack('i',header_size)
    print(obj,len(obj))
     
    b、 服务端
    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() #(连接对象,客户端的ip和端口)
        print(client_addr)
        while True:
            try:
                cmd=conn.recv(1024)
                obj=subprocess.Popen(cmd.decode('utf-8'),
                                     shell=True,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE
                                     )
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
     
                # 1、制作报头
                header_dic={
                    'total_size':len(stdout) + len(stderr),
                    'md5':'123svsaef123sdfasdf',
                    'filename':'a.txt'
                }
                header_json = json.dumps(header_dic)
                header_bytes = header_json.encode('utf-8')
     
                # 2、先发送报头的长度
                header_size=len(header_bytes)
                conn.send(struct.pack('i',header_size))
     
                # 3、发送报头
                conn.send(header_bytes)
     
                # 4、发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
     
        conn.close()
    server.close()
     
    c、 客户端
    from socket import *
    import struct
    import json
     
    client=socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    # print(client)
     
    while True:
        cmd=input('>>>: ').strip()
        if not cmd:continue
        client.send(cmd.encode('utf-8'))
        #1、先收报头的长度
        header_size=struct.unpack('i',client.recv(4))[0]
     
        #2、接收报头
        header_bytes=client.recv(header_size)
     
        #3、解析报头
        header_json=header_bytes.decode('utf-8')
        header_dic=json.loads(header_json)
        print(header_dic)
     
        total_size=header_dic[ 'total_size']
        # print(total_size) #1025
        #4、根据报头内的信息,收取真实的数据
     
        recv_size=0
        res=b''
        while recv_size < total_size:
            recv_data=client.recv(1024)
            res+=recv_data
            recv_size+=len(recv_data)
     
        print(res.decode('gbk'))
    client.close()
     
    五、远程执行命令的程序
    a、 客户端
    from socket import *
     
    client=socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    # print(client)
     
    while True:
        cmd=input('>>>: ').strip()
        if not cmd:continue
        client.send(cmd.encode('utf-8'))
        # print('has send')
        res=client.recv(14744)
        # print('has recv')
        print(len(res))
        print(res.decode('gbk'))
     
    client.close()
     
    b、 服务端
    from socket import *
    import subprocess
     
    server=socket(AF_INET,SOCK_STREAM)
    server.bind(('127.0.0.1',8080))
    server.listen(5)
     
    while True:
        conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
        print(client_addr)
        while True:
            try:
                cmd=conn.recv(1024)
                obj=subprocess.Popen(cmd.decode('utf-8'),
                                     shell=True,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE
                                     )
                stdout=obj.stdout.read()
                stderr=obj.stderr.read()
     
                # 先发送数据的长度
                total_size=len(stdout) + len(stderr)
                conn.send(total_size)
     
                # 发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
            except ConnectionResetError:
                break
     
        conn.close()
    server.close()
    #交互式命令或是cd。。一般要在一台机器上,不推荐远程调控。如若强行要求,不要直接执行,而是要模拟执行,并将模拟结果发回。
  • 相关阅读:
    【技术贴】解决Eclipse编译java源文件之后没有生成class文件|找不到class文件
    小米1s 正式为我服役,纪念一下。
    解决servlet的out输出流html中文乱码
    小米1s充电时屏幕一直亮着不关闭的解决办法
    今天遇到的问题分析
    java操作Excel(org.apache.poi.hssf.usermodel)
    HTML滚动文字代码 marquee标签
    MIUI小米 卸载金山安全服务
    Oracle DBA 逻辑备份试题
    Oracle DBA结构试题1
  • 原文地址:https://www.cnblogs.com/zhangyaqian/p/py20180420.html
Copyright © 2011-2022 走看看