• 半连接数,粘包问题,自定义报头


    今日内容:

      1.半连接数

      2.粘包问题

      3.自定义报头

    1. 半连接数

    三次握手没有完成 称之为半连接

    原因1 恶意客户端没有返回第三次握手信息

    原因2 服务器没空及时处理你的请求

    socket中 listen(半连接最大数量)

    2. 粘包问题

     

    TCP流式协议, 数据之间没有分界, 就像水 一杯水和一杯牛奶倒在一起了!

     

    UDP 用户数据报协议

     

    粘包 仅发生在TCP协议中

     

    1. 发送端 发送的数据量小 并且间隔短 会粘

    2. 接收端 一次性读取了两次数据的内容 会粘

    3. 接收端 没有接收完整 剩余的内容 和下次发送的粘在一起

     

    无论是那种情况,其根本原因在于 接收端不知道数据到底有多少

     

    解决方案就是 提前告知接收方 数据的长度

     

    粘包问题解决方案:

     

    先发长度给对方 再发真实数据

     

     

     

    #发送端

     

    1.使用struct 将真实数据的长度转为固定的字节数据

     

    2.发送长度数据

     

    3.发送真实数据

     

    接收端

     

    1.先收长度数据 字节数固定

     

    2.再收真实数据 真实可能很长 需要循环接收

     

    发送端和接收端必须都处理粘包 才算真正的解决了

    案例:远程CMD程序

    服务器:

    import socket
    import struct
    import subprocess
    
    # 创建服务器套接字对象
    server = socket.socket()
    # 绑定IP和端口
    server.bind(('192.168.13.29', 8080))
    server.listen(1)
    while True:
        # 完成与客户端的三次握手
        conn, address = server.accept()
        while True:
            try:
                # 获取客户端的报头
                cmd = conn.recv(4)
                if not cmd:
                    break
                # 拿到的是解码后的字符串
                num = struct.unpack('i', cmd)[0]
                # 设定缓冲区的大小
                buffer_size = 1024
                # 设置已经接收的大小
                recv_size = 0
                # 设置初始信息量
                info = b''
                while True:
                    # 查看管道中剩余的数量,如果大于1024,那就继续接收1024
                    if num - recv_size >= buffer_size:
                        temp = conn.recv(buffer_size)
                        print('123')
                    else:
                        # 否则,则接收总数减去已接受所剩余的数量,解决粘包问题
                        temp = conn.recv(num - recv_size)
                        print('123')
                    info += temp
                    recv_size += len(temp)
                    if num == recv_size:
                        break
                p = subprocess.Popen(info.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                out_info = p.stdout.read()
                err_info = p.stderr.read()
                all_info = out_info + err_info
    
                len_size = len(all_info)
                byte_len = struct.pack('i', len_size)
    
                conn.send(byte_len)
    
                conn.send(all_info)
            except Exception as f:
                print('异常原因:', f)
                break
        conn.close()

    客户端:

    import socket
    import struct
    
    client = socket.socket()
    
    # 创建连接
    client.connect(('192.168.13.29', 8080))
    
    while True:
        try:
            cmd = input('请输入指令:').strip()
            if not cmd: break
            # 拿到指令转换为字节的长度
            byte_cmd = struct.pack('i', len(cmd.encode('utf-8')))
            # 先发长度
            client.send(byte_cmd)
            # 再发指令
            client.send(cmd.encode('utf-8'))
    
            # 然后收取服务端的报头
            info_size = client.recv(4)
            # 将报头解码为原数字类型,获得数据的总长度
            len_info = struct.unpack('i', info_size)[0]
    
            # 设置接收数据的大小
            buffer_size = 2048
            # 设置已经接收的大小
            recv_size = 0
            # 设置初始信息量
            info = b''
            while True:
                if len_info - recv_size > buffer_size:
                    temp = client.recv(buffer_size)
                else:
                    temp = client.recv(len_info - recv_size)
                recv_size += len(temp)
                info += temp
                if recv_size == len_info: break
            print(info.decode('gbk'))
        except Exception as f:
            print('异常原因是:%s' % f)
            client.close()

    自定义报头:

    当需要在传输数据时 传呼一些额外参数时就需要自定义报头

    报头本质是一个json 数据

    具体过程如下:

    发送端

    1 发送报头长度

    2 发送报头数据 其中包含了文件长度 和其他任意的额外信息

    3 发送文件内容

     

    接收端

    1.接收报头长度

    2.接收报头信息

    3.接收文件内容

    案例:服务器与客户端实现文件传输

    服务器:

    import socket
    import os
    import json
    import struct
    
    # 获得套接字对象
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 绑定IP
    server.bind(('192.168.13.29', 8080))
    
    server.listen(2)
    while True:
        conn, address = server.accept()
        f = None
        try:
            path = r'D:python练习python十万行代码学day32机密数据文件.txt'
            # 获取文件大小
            file_size = os.path.getsize(path)
    
            # 制作报头
            file_info = {'file_name': '机密数据文件.txt', 'file_size': file_size}
            # 使用json序列化报头成为字符串并编码,编程二进制
            file_str = json.dumps(file_info).encode('utf-8')
    
            # 发送报头长度
            conn.send(struct.pack('q', len(file_str)))
    
            # 发送报头
            conn.send(file_str)
    
            # 发送文件
            f = open(path, 'rb')
            while True:
                temp = f.read(1024)
                if not temp:
                    break
                conn.send(temp)
            print('文件发送完毕')
        except Exception as f:
            print('异常原因%s' % f)
        finally:
            if f:f.close()
        conn.close()

    客户端:

    import socket
    import struct
    import json
    
    # 获取客户端套接字对象
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 获得服务器IP和端口
    try:
        client.connect(('192.168.13.29', 8080))
        print('连接成功')
    
        # 接收报头的长度 head_size就是一个pack好的字典,8个字节长度
        head_size = struct.unpack('q', client.recv(8))[0]
    
        # 接收报头数据
        head_str = client.recv(head_size).decode('utf-8')  # 得到报头字符串形式的字典
        file_info = json.loads(head_str)  # 使用json反序列化得到字典
        print('报头数据%s' % file_info)
        file_size = file_info.get('file_size')  # 获取文件大小
        file_name = file_info.get('file_name')  # 获取文件名称
    
    
        # 再接收文件内容
        revc_size = 0  # 已接收的长度
        buffer_size = 2048  # 每次接收数据的大小
        f = open(file_name, 'wb')
        while True:
            if file_size - revc_size >= buffer_size:
                temp = client.recv(buffer_size)
            else:
                temp = client.recv(file_size - revc_size)
            f.write(temp)
            revc_size += len(temp)
            print('已经下载%1f%%' % (revc_size / file_size * 100))
            if file_size == revc_size: break
        f.close()
    except Exception as f:
        print('异常原因:%s' % f)

     

     

  • 相关阅读:
    slz关于Date类
    slzJDK1.8的环境变量配置
    slz关于下载Eclipse(绿色版,无需安装)及参数的设置
    slzjdk1.8安装包的下载
    slzTomcat9.0的下载(绿色版,无需安装)及环境变量的配置
    第一个dp51程序实现拉幕效果
    tsql中的sleepwaitfor
    删除WorkSheet时不提示对话框 WorkSheet.Delete
    如何写标准的连接字符串
    如何在Foxpro中调用Win32 api函数
  • 原文地址:https://www.cnblogs.com/liguodeboke/p/10946846.html
Copyright © 2020-2023  润新知