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


                                                                           半连接数, 粘包问题和自定义一个报头

    半连接数:

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

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

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

    半连接数的设置是在socket中的listen中设置的

    如:sercice.listen(5) 这个半连接就为5,可以自定

     

    粘包问题:

     

    粘包 仅发生在TCP协议中

     

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

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

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

     

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

     

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

    解决方案

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

    #发送端

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

    2.发送长度数据

    3.发送真实数据

    接收端

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

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

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

    案例: 远程CMD程序

    客户端:

    import socket
    from 二_CMD程序 import smallTool
    import struct
    
    client = socket.socket()
    try:
        client.connect(("127.0.0.1",1688))
        print("链接成功!")
        while True:
            msg = input("请输入要执行指令:").strip()
            if msg == "q": break
            if not msg: continue
            # 发送指令
            # 先发长度
            len_bytes = struct.pack("q",len(msg.encode("utf-8")))
            client.send(len_bytes)
            # 在发指令
            client.send(msg.encode("utf-8"))
    
            data = smallTool.recv_data(client)
            print(data.decode("GBK"))
    
        client.close()
    except ConnectionRefusedError as e:
        print("链接服务器失败了!",e)
    except ConnectionResetError as e:
        print("服务器挂了!", e)
        client.close()
    View Code

    服务端:

    import socket
    import subprocess
    import struct
    from 二_CMD程序 import  smallTool
    
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(("127.0.0.1",1688))
    server.listen()
    # back
    
    while True:
        # socket,addr一个元组 客户端的ip和port
        client,addr = server.accept()
        print("客户端链接成功!")
        # 循环收发数据
        while True:
            try:
                cmd = smallTool.recv_data(client)
                if not cmd:
                    break
                print(cmd)
    
                p = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                # 不要先读err错误信息  它会卡主  原因不详  linux不会有问题  tasklist  netstat - ano啥的
                data = p.stdout.read()
                err_data = p.stderr.read()
    
                len_size = len(data) + len(err_data)
                print("服务器返回了: %s " %  len_size)
    
                len_bytes = struct.pack("q",len_size)
    
                # 在发送真实数据前先发送 长度
                client.send(len_bytes)
    
                # 返回的结果刚好就是二进制
                # 发送真实数据
                client.send(data + err_data)
    
    
            except ConnectionResetError as e:
                print("客户端了挂了!",e)
                break
        client.close()
    View Code

    自定义报头

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

    报头本质是一个json 数据

    具体过程如下:

    发送端

    1 发送报头长度

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

    3 发送文件内容

     

    接收端

    1.接收报头长度

    2.接收报头信息

    3.接收文件内容

    案例:

    服务器:

    import socket
    import os
    import struct
    import json
    """
    客户端接链成功我就给你发个文件过去  
    固定的文件下载
    
    """
    
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.bind(("127.0.0.1",1688))
    server.listen()
    # back
    
    while True:
        # socket,addr一个元组 客户端的ip和port
        client,addr = server.accept()
        print("客户端链接成功!")
        f = None
        try:
    
            path = r"F:2.半链接数.mp4"
            file_size = os.path.getsize(path)
    
            # 我想把文件名发过去
            file_info = {"file_name":"半链接数.mp4","file_size":file_size,"md5":"xxxxxxxxx"}
    
            json_str = json.dumps(file_info).encode("utf-8")
    
            # 发送报头长度
            client.send(struct.pack("q",len(json_str)))
    
            # 发报头
            client.send(json_str)
    
            # 发文件了
            # 发送文件数据
            f = open(path,"rb")
            # 循环发送文件内容   每次发2048
            while True:
                temp = f.read(2048)
                if not temp:
                    break
                client.send(temp)
            print("文件发送完毕!")
    
        except Exception as e:
            print("出问题了",e)
        finally:
            if f:f.close()
        client.close()
    
        # 无论是否抛出异常 文件都要关闭
    
    #server.close()
    
    # 用户可以指定要下载什么文件 FTP
    View Code

    客户端:

    """
    客户端输入指令
    服务器接收指令并执行  最后返回执行结果
    """
    
    import socket
    import struct
    import json
    
    client = socket.socket()
    try:
        client.connect(("127.0.0.1",1688))
        print("链接成功!")
    
        # 1.先收报头长度
        head_size = struct.unpack("q",client.recv(8))[0]
    
        # 2.收报头数据
        head_str = client.recv(head_size).decode("utf-8")
        file_info = json.loads(head_str)
        print("报头数据:",file_info)
        file_size = file_info.get("file_size")
        file_name = file_info.get("file_name")
    
    
        # 3.再收文件内容
        # 已接收大小
        recv_size = 0
        buffer_size = 2048
        f = open(file_name,"wb")
        while True:
            if file_size - recv_size >= buffer_size:
                temp = client.recv(buffer_size)
    
            else:
                temp = client.recv(file_size - recv_size)
            f.write(temp)
            recv_size += len(temp)
            print("已下载:%s%%" % (recv_size / file_size * 100))
            if recv_size == file_size:
                break
        f.close()
    except ConnectionRefusedError as e:
        print("链接服务器失败了!",e)
    View Code

     

     

     

     

     

  • 相关阅读:
    Python 操作 MySQL 之 pysql 与 SQLAchemy
    Python 列表元素里面含有字典或者列表进行排序
    Windows PyCharm QtDesigner/pyuic5配置
    Python之科学运算Pandas模块
    Linux 主要目录速查表
    php读取文件大小
    上传视频云点播和查看视频
    wordppt转pdf
    微信登录
    微信公众号
  • 原文地址:https://www.cnblogs.com/WBaiC1/p/10946663.html
Copyright © 2020-2023  润新知