• python之socket编程3


    1 什么是粘包

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

    应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向连接的,面向流的,收发两端都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法,将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这也是容易出现粘包问题的原因;

    而UDP面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据。

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

    两种情况下会发生粘包

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

    (2)接收方不及时收取缓冲区的包,造成多个包接收,客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包

    #服务端
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.0.0.1",8080)
    phone.bind(ip_port)
    phone.listen(5)
    conn,addr=phone.accept()
    data1=conn.recv(1024)
    data2=conn.recv(1024)
    print("第1个包",data1)
    print("第2个包",data2)
    
    #客户端
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.0.0.1",8080)
    phone.connect(ip_port)
    phone.send("helloworld".encode("utf8"))
    phone.send("SB".encode("utf8"))
    
    >>
    第1个包 b'helloworldSB'#产生粘包现象
    第2个包 b''
    

    使用时间模块,并不能够彻底解决问题

    #服务端
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.0.0.1",8080)
    phone.bind(ip_port)
    phone.listen(5)
    conn,addr=phone.accept()
    data1=conn.recv(1024)
    data2=conn.recv(1024)
    print("第1个包",data1)
    print("第2个包",data2)
    
    
    #客户端
    import socket,time
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.0.0.1",8080)
    phone.connect(ip_port)
    phone.send("helloworld".encode("utf8"))
    time.sleep(3)
    phone.send("SB".encode("utf8"))
    
    >>
    第1个包 b'helloworld'#send先执行一次后,发送至客户端内存
    第2个包 b'SB'#延迟三秒后,,再执行发送
    #服务端
    import socket
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.0.0.1",8080)
    phone.bind(ip_port)
    phone.listen(5)
    conn,addr=phone.accept()
    data1=conn.recv(1)
    data2=conn.recv(1024)
    print("第1个包",data1)
    print("第2个包",data2)
    	
    #客户端
    import socket,time
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.0.0.1",8080)
    phone.connect(ip_port)
    
    
    phone.send("helloworld".encode("utf8"))
    time.sleep(3)
    phone.send("SB".encode("utf8"))
    >>
    第1个包 b'h'  #recv第一次只取一个字节
    第2个包 b'elloworld'  #第二次再次执行 ,”SB”还在服务器的内存中
    

    2 自定制报头解决粘包问题

    数据封装报头:

    固定长度

    包含对将要发送数据的描述信息

    struct模块
    import struct
    print(struct.pack("i",111))
    >>
    b'ox00x00x00'   #转成字节模式
    
    import struct
    res=struct.pack("i",111)#struct.pack 打包
    print(len(res))
    >>
    4  #转成字节长度为固定4
    
    import struct
    res=struct.pack("i",111)
    # print(len(res))
    print(struct.unpack("i",res))#解包
    (111,)#获得以元组形式的结果
    

    简单实现:

    #服务端
    import socket
    import struct
    import subprocess
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.168.0.1",8080)
    phone.bind(ip_port)
    phone.listen(5)
    #连接循环
    while True:
        conn,addr=phone.accept()
        print("cline addr",addr)
        #通讯循环
        while True:
            try:
                cmd=conn.recv(1024)
                res=subprocess.Popen(cmd.decode("utf8"),
                                     shell=True,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
                out_res=res.stdout.read()
                err_res=res.stderr.read()
                data_size=len(out_res)+len(err_res)
                #发送报头
                conn.send(struct.pack("i",data_size))
                #发送数据部分
                conn.send(out_res)
                conn.send(err_res)
            except Exception:
                break
        conn.close()
    phone.close()
    
    #客户端
    import socket
    import struct
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.168.0.1",8080)
    phone.connect(ip_port)
    #通信循环
    while True:
        #发消息
        cmd=input(">>").strip()
        if not cmd:continue
        phone.send(bytes(cmd,encoding="utf8"))
        #收报头
        baotou=phone.recv(4)
        data_size=struct.unpack("i",baotou)[0]
        #收数据
        recv_size=0
        recv_data=b""
        while recv_size<data_size:
            data=phone.recv(1024)
            recv_size+=len(data)
            recv_data+=data
        print(recv_data.decode("gbk"))
    phone.close()
    

    完全解决:

    #服务端
    import socket
    import struct
    import subprocess
    import json
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.168.0.1",8080)
    phone.bind(ip_port)
    phone.listen(5)
    #连接循环
    while True:
        conn,addr=phone.accept()
        print("cline addr",addr)
        #通讯循环
        while True:
            try:
                cmd=conn.recv(1024)
                res=subprocess.Popen(cmd.decode("utf8"),
                                     shell=True,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
                out_res=res.stdout.read()
                err_res=res.stderr.read()
                data_size=len(out_res)+len(err_res)
                head_dic={"data_size":data_size}
                head_json=json.dumps(head_dic)
                head_bytes=head_json.encode("utf8")
                #发送报头
                head_len=len(head_bytes)
                conn.send(struct.pack("i",data_size))
                conn.send(head_bytes)
                #发送数据部分
                conn.send(out_res)
                conn.send(err_res)
            except Exception:
                break
        conn.close()
    phone.close()
    
    #客户端
    import socket
    import struct
    import json
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    ip_port=("127.168.0.1",8080)
    phone.connect(ip_port)
    #通信循环
    while True:
        #发消息
        cmd=input(">>").strip()
        if not cmd:continue
        phone.send(bytes(cmd,encoding="utf8"))
        #收报头的长度
        head_struct=phone.recv(4)
        head_len=struct.unpack("i",head_len)[0]
    
        #再收报头
        head_bytes=phone.recv(head_len)
        head_json=head_bytes.decode("utf8")
        head_dic=json.loads(head_json)
        print(head_dic)
        data_size=head_dic["data_size"]
    
        #收数据
        recv_size=0
        recv_data=b""
        while recv_size<data_size:
            data=phone.recv(1024)
            recv_size+=len(data)
            recv_data+=data
        print(recv_data.decode("gbk"))
    phone.close()
    

      

     

  • 相关阅读:
    LeetCode: Maximum Product Subarray 解题报告
    LeetCode: Populating Next Right Pointers in Each Node II 解题报告
    LeetCode: Populating Next Right Pointers in Each Node 解题报告
    LeetCode: Word Search 解题报告
    C语言文件操作
    多线程
    C语言函数指针的使用
    进程
    网络编程
    进程间通信——管道通信
  • 原文地址:https://www.cnblogs.com/asaka/p/6820627.html
Copyright © 2020-2023  润新知