Python 第三十章 粘包现象

亡梦爱人 提交于 2019-11-27 11:00:28
什么是粘包?
有缓存区,缓存区里放响应的字节,满了后,将剩下的存起来
等下次再接受信息的时候,先将上次剩下的输出
再次接受信息的时候,将上次没有输出的信息输出
    在TCP协议中,发送方发送的若干包数据到接收方,接收时粘成一包
从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

为什么设置缓冲区?
断网后数据会丢失
下载数据断网后再联网做个缓冲控制速度均衡
1、暂时存储一些数据
2、网络有波动,保证数据的收发稳定和均速
确定:造成了粘包现象之一

什么情况下出现粘包
1、send:数据过大,大过对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余数据
2、send:连续短暂的send多次(传送数据量较小),统一存到了recv缓存区;再统一发送出去
多少算小

展示一些收发的内容
只要send就要转成bytes类型

 如何解决粘包现象
 服务端发一次数据:10000字节
 客户端接受数据时,循环接收,每次最多接收1024个字节,直到将10000所有的字节全部接收完毕,
 将接收的数据拼接在一起,最后解码

 1、遇到的问题:recv的次数无法确定
    发送总具体数据之前,先给一个总数据的长度,然后再发总数据
    客户端先接收一个长度,再循环recv控制循环的条件就是只要接收的数据小于总长度,就一直循环
 2、遇到的问题:
    总数据的长度转成字节
    要解决将不固定长度的int类型转成固定的bytes字节,且还能翻转回来
    要将total_size int类型转成bytes类型才能发送
    
    服务端:
    conn.send(total_size)
    conn.send(result)
    total_size int 类型
    
    客户端:
    total_size_bytes = phone.recv(4)
    total_size
    data = b''
    while len(data) < total_size:
        data = data + phone.recv(1024)
    将total_size int 类型转化成bytes类型才可以发送
    将不固定长度的int类型转化成固定长度的bytes并且可以翻转回来
    387 --> str(387)'387' -->bytes b'387' 长度 3bytes
    4185 --> str(4185)'4185' --> bytes b'387' 长度  4bytes

low 版解决方案

server端

# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
import socket
import subprocess
import struct
phone = socket.socket()

phone.bind(('127.0.0.1',8848))

phone.listen(2)
# listen: 2 允许有两个客户端加到半链接池,超过两个则会报错

while 1:
    conn,addr = phone.accept()  # 等待客户端链接我,阻塞状态中
    # print(f'链接来了: {conn,addr}')

    while 1:
        try:

            from_client_data = conn.recv(1024)  # 接收命令


            if from_client_data.upper() == b'Q':
                print('客户端正常退出聊天了')
                break

            obj = subprocess.Popen(from_client_data.decode('utf-8'),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,

                                   )
            result = obj.stdout.read() + obj.stderr.read()
            total_size = len(result)
            print(f'总字节数:{total_size}')

            # 1. 制作固定长度的报头
            head_bytes = struct.pack('i',total_size)

            # 2. 发送固定长度的报头
            conn.send(head_bytes)

            # 3. 发送总数据
            conn.send(result)
        except ConnectionResetError:
            print('客户端链接中断了')
            break
    conn.close()
phone.close()




# import struct
# # 将一个数字转化成等长度的bytes类型。
# ret = struct.pack('i', 180000000)
# # print(ret, type(ret), len(ret))
#
# # 通过unpack反解回来
# ret1 = struct.unpack('i',ret)[0]
# # print(ret1)
# print(ret1, type(ret1))


# 总数据:总数据长度

# s1 = 'fjdslf太白金星jsfk疯狂夺金分离式的疯狂的数量方式登记拉开lagfdkjglkhjklh'
# b1 = s1.encode('utf-8')
# print(b1)
# print(len(b1))

client

import socket
import struct
phone = socket.socket()

phone.connect(('127.0.0.1',8848))
while 1:
    to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
    if not to_server_data:
        # 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
        print('发送内容不能为空')
        continue
    phone.send(to_server_data)
    if to_server_data.upper() == b'Q':
        break

    # 1. 接收报头
    head_bytes = phone.recv(4)
    # 2. 反解报头
    total_size = struct.unpack('i',head_bytes)[0]

    total_data = b''

    while len(total_data) < total_size:
        total_data += phone.recv(1024)

    print(len(total_data))
    print(total_data.decode('gbk'))

phone.close()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!