• python-day30--粘包


    一、 什么是粘包

    1.须知:只有TCP有粘包现象,UDP永远不会粘包

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

    二、两种情况下会发生粘包。

    1.发送数据时间间隔很短,数据了很小,会合到一起,产生粘包

    1 from socket import *
    2 import time
    3 phone=socket(AF_INET,SOCK_STREAM)
    4 phone.connect(('127.0.0.1',8080))
    5 
    6 phone.send('helloworld'.encode('utf-8'))
    7 phone.send('egon'.encode('utf-8'))
    客户端

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

    from socket import *
    import time
    phone=socket(AF_INET,SOCK_STREAM)
    phone.connect(('127.0.0.1',8080))
    
    phone.send('helloworld'.encode('utf-8'))
    time.sleep(5)
    phone.send('egon'.encode('utf-8'))
    客户端
    from socket import *
    phone=socket(AF_INET,SOCK_STREAM)
    phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    conn,client_addr=phone.accept()
    
    data1=conn.recv(8)
    print('data1: ',data1)
    data2=conn.recv(20)
    print('data2:',data2)
    data3=conn.recv(20)
    print('data3:',data3)
    # 结果
    # data1:  b'hellowor'
    # data2: b'ld'
    # data3: b'egon'
    服务端

    三、

    拆包的发生情况

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。

    补充问题一:为何tcp是可靠传输,udp是不可靠传输

    基于tcp的数据传输请参考我的另一篇文章http://www.cnblogs.com/linhaifeng/articles/5937962.html,tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的

    而udp发送数据,对端是不会返回确认信息的,因此不可靠

    补充问题二:send(字节流)和recv(1024)及sendall

    recv里指定的1024意思是从缓存里一次拿出1024个字节的数据

    send的字节流是先放入己端缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失

    四、解决粘包的low比处理方法

     1 import socket
     2 import subprocess
     3 import struct
     4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
     6 phone.bind(('127.0.0.1',8082)) #绑定手机卡
     7 phone.listen(5) #开机
     8 
     9 print('starting...')
    10 while True: #链接循环
    11     conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    12     print('-------->',conn,client_addr)
    13     #收,发消息
    14     while True:#通信循环
    15         try:
    16             cmd=conn.recv(1024)
    17             if not cmd:break #针对linux
    18             #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
    19             #。。。。
    20             res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
    21                                    stdout=subprocess.PIPE,
    22                                    stderr=subprocess.PIPE)
    23             stdout=res.stdout.read()
    24             stderr=res.stderr.read()
    25             #先发报头(转成固定长度的bytes类型)
    26             header = struct.pack('i',len(stdout)+len(stderr))
    27             conn.send(header)
    28             #再发送命令的结果5
    29             conn.send(stdout)
    30             conn.send(stderr)
    31         except Exception:
    32             break
    33     conn.close() #挂电话
    34 phone.close() #关机
    服务端
     1 import socket
     2 import struct
     3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     4 phone.connect(('127.0.0.1',8082)) #绑定手机卡
     5 #发,收消息
     6 while True:
     7     cmd=input('>>: ').strip()
     8     if not cmd:continue
     9     phone.send(cmd.encode('utf-8'))
    10     #先收报头
    11     header_struct=phone.recv(4)
    12     unpack_res = struct.unpack('i', header_struct)
    13     total_size=unpack_res[0]
    14     #再收数据
    15     recv_size=0 #10241=10240+1
    16     total_data=b''
    17     while recv_size < total_size:
    18         recv_data=phone.recv(1024)
    19         recv_size+=len(recv_data)
    20         total_data+=recv_data
    21     print(total_data.decode('gbk'))
    22 phone.close()
    客户端

     五、完美解决tcp协议粘包问题

     1 import socket
     2 import subprocess
     3 import struct
     4 import json
     5 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     6 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
     7 phone.bind(('127.0.0.1',8082)) #绑定手机卡
     8 phone.listen(5) #开机
     9 
    10 print('starting...')
    11 while True: #链接循环
    12     conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    13     print('-------->',conn,client_addr)
    14 
    15     #收,发消息
    16     while True:#通信循环
    17         try:
    18             cmd=conn.recv(1024)
    19             if not cmd:break #针对linux
    20             #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
    21             #。。。。
    22             res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
    23                                    stdout=subprocess.PIPE,
    24                                    stderr=subprocess.PIPE)
    25             stdout=res.stdout.read()
    26             stderr=res.stderr.read()
    27             #制作报头
    28             header_dic = {
    29                 'total_size': len(stdout)+len(stderr),
    30                 'filename': None,
    31                 'md5': None}
    32 
    33             header_json = json.dumps(header_dic)
    34             header_bytes = header_json.encode('utf-8')
    35             #发送阶段
    36             #先发报头长度
    37             conn.send(struct.pack('i',len(header_bytes)))
    38             #再发报头
    39             conn.send(header_bytes)
    40 
    41             #最后发送命令的结果
    42             conn.send(stdout)
    43             conn.send(stderr)
    44         except Exception:
    45             break
    46     conn.close() #挂电话
    47 phone.close() #关机
    服务端
     1 import socket
     2 import struct
     3 import json
     4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
     5 phone.connect(('127.0.0.1',8082)) #绑定手机卡
     6 
     7 #发,收消息
     8 while True:
     9     cmd=input('>>: ').strip()
    10     if not cmd:continue
    11 
    12     phone.send(cmd.encode('utf-8'))
    13     #先收报头的长度
    14     header_len=struct.unpack('i',phone.recv(4))[0]
    15 
    16     #再收报头
    17     header_bytes=phone.recv(header_len)
    18     header_json=header_bytes.decode('utf-8')
    19     header_dic=json.loads(header_json)
    20     total_size=header_dic['total_size']
    21 
    22     #最后收数据
    23     recv_size=0 #10241=10240+1
    24     total_data=b''
    25     while recv_size < total_size:
    26         recv_data=phone.recv(1024)
    27         recv_size+=len(recv_data)
    28         total_data+=recv_data
    29     print(total_data.decode('gbk'))
    30 phone.close()
    客户端
  • 相关阅读:
    Gridview布局界面练习Base Adapter
    用Handler图片轮播练习
    登陆界面用户名,密码存储以及再次登陆自动填充用户名、密码
    登陆界面练习
    进度条练习
    上下文菜单项(contextMenu)----长按按钮弹出菜单项
    菜单项的练习
    Android ImageView的scaleType属性与adjustViewBounds属性
    android单选框和复选框(练习)
    QQ登陆模拟练习
  • 原文地址:https://www.cnblogs.com/liuwei0824/p/7413463.html
Copyright © 2020-2023  润新知