黏包现象及其解决方法

1.黏包现象

                      服务端:                  客户端:
第一次 dir             数据 < 1024            接收所有数据
第二次 ipconfig        数据 > 1024            接收1024个字节
第三次 dir             数据 < 1024            接收上次剩下的字节
TCP协议的流式协议,数据与水流一样源源不断 => 粘包现象
黏包现象的根本原因:缓冲区
缓冲区的作用:如果网络出现短暂的异常或者波动,接收数据就会出现出现短暂的中断,影响下载或上传的效率
什么情况下黏包现象产生黏包?
1.recv会产生黏包(如果recv接受的数据量(1024)小于发送的数据量,第一次只能接收规定的数据量1024,第二次接受剩余的数据量)
2.send也可能发生黏包现象(连续send少量的数据发到输出缓冲区,由于缓冲区的机制,可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络)

2.解决黏包的方案

思路分析:
1.当第二次给服务器发送命令之前,应该循环recv直至将所用的数据全部取完
3000bytes  =>  recv3次
5000bytes  =>  recv5次
30000bytes  =>  recv30次
2.限制循环次数:当发送的总bytes个数,与接收的总bytes个数相等时,循环结束
3.如何获取发送的总bytes个数  =>  len()
4.总bytes个数的类型
将int转化成bytes,但无论总字节个数是多少,都应确保转化成固定长度的bytes,以此方便获取头部信息
通过struct将总bytes个数转化成固定的4个字节  =>  low版
总bytes个数过大时,使用struct会报错    =>   旗舰版
low版:
服务端
import socket
import subprocess
import struct
phone = socket.socket()
phone.bind(('127.0.0.1',8848))
phone.listen(5)
conn,addr = phone.accept()
while 1:
    try:
        client_data=conn.recv(1024)
        obj = subprocess.Popen(client_data.decode('utf-8'),
                       shell = True,
                       stdout = subprocess.PIPE,
                       stderr = subprocess.PIPE)
        result = obj.stdout.read()+obj.stderr.read()
        total_size = len(result)
        total_size_bytes = struct.pack('i',total_size)
        conn.send(total_size_bytes)
        conn.send(result)
    except ConnectionResetError :
        break
conn.close()
phone.close()

客户端
import socket
import struct
phone = socket.socket()
phone.connect(('127.0.0.1',8848))
while 1:
    content = input('输入:')
    if content.upper() == 'Q':
        break
    phone.send(content.encode('utf-8'))
    head_bytes = phone.recv(4)
    total_size = struct.unpack('i',head_bytes)[0]
    total_data = b''
    while len(total_data)<total_size:
        total_data += phone.recv(1024)
    print(total_data.decode('gbk'))
phone.close()
旗舰版:
服务端
import socket
import subprocess
import struct
import json
phone = socket.socket()
phone.bind(('127.0.0.1', 8888))
phone.listen(5)
conn, addr = phone.accept()
while 1:
    try:
        cmd = conn.recv(1024)
        obj = subprocess.Popen(cmd.decode('utf-8'),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,)
        result = obj.stdout.read() + obj.stderr.read()
        result = result.decode('gbk').encode('utf-8')
        # 1. 制作报头
        head_dict = {
            'MD5': '7a6f150b83091ce20c89368641f9a137',
            'file_name': '蔡徐坤篮球秀',
            'file_size': len(result),
        }
        # 2. 将报头字典转化成json字符串
        head_dict_json = json.dumps(head_dict)
        # 3. 将json字符串 转化成bytes
        head_dict_json_bytes = head_dict_json.encode('utf-8')
        # 4. 获取报头的长度
        head_len = len(head_dict_json_bytes)
        # 5.将长度转化成固定的4个字节
        head_len_bytes = struct.pack('i',head_len)
        # 6. 发送固定的4个字节
        conn.send(head_len_bytes)
        # 7. 发送报头
        conn.send(head_dict_json_bytes)
        # 8. 发送原数据
        conn.send(result)
    except ConnectionResetError:
        break
conn.close()
phone.close()

客户端
import socket
import struct
import json
phone = socket.socket()
phone.connect(('127.0.0.1',8888))
while 1:
    content = input('输入:')
    if content.upper() == 'Q':
        break
    phone.send(content.encode('utf-8'))
    head_len_bytes = phone.recv(4)
    head_len = struct.unpack('i',head_len_bytes)[0]
    head_dict_json_bytes = phone.recv(head_len)
    head_dict_json = head_dict_json_bytes.decode('utf-8')
    head_dict = json.loads(head_dict_json)
    total_data = b''
    while len(total_data)<head_dict['file_size']:
        total_data += phone.recv(1024)
    print(total_data.decode('utf-8'))
phone.close()

猜你喜欢

转载自www.cnblogs.com/wxl1025/p/11202208.html