zoukankan      html  css  js  c++  java
  • 粘包现象(存在于tcp中)

    多个包 多个命令的结果 粘到一起了 因为recv(1024)1024限制了导致的结果

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

    参考:http://www.cnblogs.com/linhaifeng/articles/6129246.html

    粘包底层原理分析:
    1、运行一个软件和硬盘、内存、cpu这三个硬件有关

    2、启动程序:硬盘程序加载到内存启动一个软件就占一个内存空间
      操作系统本身有一个内存空间
      操作系统所占得到内存空间和软件的内存空间彼此互相隔离

    注:只有TCP有粘包现象,UDP永远不会粘包。



    3、send、recv的底层原理

    应用程序通过send将自己数据发给操作系统,操作系统调用相应硬件中的数据及程序,通过网卡传送给操作系统(recv对应的操作系统),操作系统通过相应程序将数据传送给secv
      1、send发到数据到服务端os的内存 # 慢
      2、os的内存 copy 给程序 # 快
    站在应用程序角度上:
      send: 1.数据发给本地的os # 耗时短一些
      recv:1.通知os收数据 2.os内存数据copy到应用程序内存中 # 耗时长一些

    4、send recv 总结:
    a、不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv # 一发可以对应多收 一收对应多发
    b、recv:
      wait data 耗时非常长
      copy data
         send:
      copy data
    c、数据量比较小 时间间隔比较短就合并成一个包,再发
    使用了优化方法(Nagle算法)

    5.recv 有关部门建议的不要超过 8192,再大反而会出现影响收发速度和不稳定的情况


    解决粘包现象

    用struct模块

    import struct
    
    # res = struct.pack("i", 1230)  # 第一个参数代表格式i(int)
    # print(res, type(res), len(res))
    #
    # obj = struct.unpack("i", res)
    # print(obj[0])
    
    # clienr.recv(4)
    
    res = struct.pack("l", 1200000000)  # 第一个参数代表格式l(long)
    print(res, type(res), len(res))
    

    解决粘包解释加报头


    解决粘包(简单版)

    服务端

    import socket
    import subprocess
    import struct
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # (如果机器中存在,重新用端口)应对端口占用报错情况
    # 2、绑定手机卡
    phone.bind(("127.0.0.1", 9909))   # 127.0.0.1本地地址,端口范围0-65535:其中0-1024给操作系统使用
    
    # 3、开机
    phone.listen(5)   # 5代表最大挂起连接数
    
    # 4、等电话连接
    print("starting...")
    while True:  # 循环链接
        conn, client = phone.accept()  # conn套接字对象
    
    # 5、收、发消息
        while True:    # 通讯循环
            try:
                # a、接收命令  (命令:执行系统命令)
                cmd = conn.recv(8096)  # 收1024个字节,接受数据的最大数。单位是bytes
               # if not data: break  # 仅适用于Linux操作系统(客户端断开),win 用try...except
                # b、执行命令,拿到结果
                obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
    
                # c、把命令的结果返回给客户端
                # 第一步:制作固定长度的报头(import struct)
                total_size = len(stdout) + len(stderr)
                header = struct.pack("i", total_size)
    
                # 第二步:把报头(固定长度)发送给客户端
                conn.send(header)
    
                # 第二步:再发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
    
            except ConnectionRefusedError:
                break
    
    # 6、挂电话
        conn.close()
    
    # 7、关机
    phone.close()
    

    客户端

    import socket
    import struct
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2、打电话
    phone.connect(("127.0.0.1", 9909))  # phone相当于服务端的conn
    
    # 3、发、收消息
    while True:
        # a、发命令
        cmd = input(">> ").strip()
        if not cmd:
            continue
        phone.send(cmd.encode("utf-8"))
    
        # b、拿命令结果并打印
        # 第一步:先收报头
        header = phone.recv(4)
    
        # 第二步:从报头中解析出对真实数据的描述信息(数据的长度)
        total_size = struct.unpack("i", header)[0]
        # 第三步:接受真实的数据
        recv_size = 0
        recv_data = b""
        while recv_size < total_size:
            res = phone.recv(1024)
            recv_data += res
            recv_size += len(res)
        print(recv_data.decode("gbk"))   # 系统发回的结果
    
    # 4、关闭
    phone.close()
    

      


    解决粘包(终极版)

    服务端

    import socket
    import subprocess
    import struct
    import json
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # (如果机器中存在,重新用端口)应对端口占用报错情况
    # 2、绑定手机卡
    phone.bind(("127.0.0.1", 9909))   # 127.0.0.1本地地址,端口范围0-65535:其中0-1024给操作系统使用
    
    # 3、开机
    phone.listen(5)   # 5代表最大挂起连接数
    
    # 4、等电话连接
    print("starting...")
    while True:  # 循环链接
        conn, client = phone.accept()  # conn套接字对象
    
    # 5、收、发消息
        while True:    # 通讯循环
            try:
                # a、接收命令  (命令:执行系统命令)
                cmd = conn.recv(8096)  # 收1024个字节,接受数据的最大数。单位是bytes
               # if not data: break  # 仅适用于Linux操作系统(客户端断开),win 用try...except
                # b、执行命令,拿到结果
                obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
    
                # c、把命令的结果返回给客户端
                # 第一步:制作固定长度的报头(import struct)
                header_dic = {
                    "filename": "a.text",
                    "md5": "xxdxxx",
                    "total_size": len(stdout) + len(stderr)
                }
                header_json = json.dumps(header_dic)
    
                header_bytes = header_json.encode("utf-8")
    
                # 第二步:先发送报头的长度
                conn.send(struct.pack("i", len(header_bytes)))
    
                # 第三步:再发报头
                conn.send(header_bytes)
    
                # 第四步:再发送真实的数据
                conn.send(stdout)
                conn.send(stderr)
    
            except ConnectionRefusedError:
                break
    
    # 6、挂电话
        conn.close()
    
    # 7、关机
    phone.close()
    

    客户端

    import socket
    import struct
    import json
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2、打电话
    phone.connect(("127.0.0.1", 9909))  # phone相当于服务端的conn
    
    # 3、发、收消息
    while True:
        # a、发命令
        cmd = input(">> ").strip()
        if not cmd:
            continue
        phone.send(cmd.encode("utf-8"))
    
        # b、拿命令结果并打印
        # 第一步:先收报头的长度
        header = phone.recv(4)
        header_size = struct.unpack("i", header)[0]
    
        #第二步:再接收报头信息
        header_bytes = phone.recv(header_size)
    
        # 第三步:从报头中解析出对真实数据的描述信息
        header_json = header_bytes.decode("utf-8")
        header_dic = json.loads(header_json)
        print(header_dic)
        total_size = header_dic["total_size"]
    
        # 第四步:接受真实的数据
        recv_size = 0
        recv_data = b""
        while recv_size < total_size:
            res = phone.recv(1024)
            recv_data += res
            recv_size += len(res)
        print(recv_data.decode("gbk"))   # 系统发回的结果
    
    # 4、关闭
    phone.close()
    

      

  • 相关阅读:
    Verilog HDL的程序结构及其描述
    VerilogHDL概述与数字IC设计流程学习笔记
    常用算法与设计模式
    DOM笔录
    JavaScript笔录
    Windows系统版本型号MSDN版、OEM版、RTM版、VOL版区别
    Yaml学习笔录
    Linux关闭iptables以及selinux
    Centos配置163YUM源
    utf8 和 UTF-8 在使用中的区别
  • 原文地址:https://www.cnblogs.com/fantsaymwq/p/10051260.html
Copyright © 2011-2022 走看看