Python网络编程03/ low版解决粘包问题
Posted liubing8
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python网络编程03/ low版解决粘包问题相关的知识,希望对你有一定的参考价值。
目录
Python网络编程03/ low版解决粘包问题
1.操作系统的缓存区
1.为什么存在缓冲区
1. 暂时存储一些数据.
2. 缓冲区存在如果你的网络波动,保证数据的收发稳定,匀速.
缺点: 造成了粘包现象之一.
2.基于TCP协议的socket循环通信
2.1 服务端(server)
# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen()
# listen: 允许5个人链接我,剩下的链接也可以链接,等待.
#
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
# print(f'链接来了: conn,addr')
#
# while 1:
# try:
# from_client_data = conn.recv(1024) # 最多接受1024字节
#
# if from_client_data.upper() == b'Q':
# print('客户端正常退出聊天了')
# break
#
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
# to_client_data = input('>>>').strip().encode('utf-8')
# conn.send(to_client_data)
# except ConnectionResetError:
# print('客户端链接中断了')
# break
# conn.close()
# phone.close()
2.2客户端(client)
# import socket
#
# 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
# from_server_data = phone.recv(1024) # 最多接受1024字节
# print(f'来自服务端消息:from_server_data.decode("utf-8")')
#
# phone.close()
#
# # s1 = 'q'
# # s2 = b'q'
# # s3 = '中国'
# # print(s3.encode('utf-8'))
# # print(type(s1),type(s2))
#
# # s1 = 'q'
# # print(s1.encode('utf-8'))
#
# # bytes类型:
# # ASCII字符: 在字符串前面b''
# # 非ASCII字符: encode 转化成 bytes类型
3.基于TCP协议的socket链接+循环 通信
3.1服务端(server)
# import socket
#
# 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) # 最多接受1024字节
#
# if from_client_data.upper() == b'Q':
# print('客户端正常退出聊天了')
# break
#
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
# to_client_data = input('>>>').strip().encode('utf-8')
# conn.send(to_client_data)
# except ConnectionResetError:
# print('客户端链接中断了')
# break
# conn.close()
# phone.close()
3.2 客户端(client)
# import socket
#
# 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
# from_server_data = phone.recv(1024) # 最多接受1024字节
# print(f'来自服务端消息:from_server_data.decode("utf-8")')
#
# phone.close()
4.基于TCP协议的socket应用实例:执行远程命令
4.1服务端(server)
# import socket
# import subprocess
# 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) # 最多接受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()
#
# conn.send(result)
# except ConnectionResetError:
# print('客户端链接中断了')
# break
# conn.close()
# phone.close()
#
#
#
#
# # shell: 命令解释器,相当于调用cmd 执行指定的命令。
# # stdout:正确结果丢到管道中。
# # stderr:错了丢到另一个管道中。
# # windows操作系统的默认编码是gbk编码。
#
4.2客户端(client)
# import socket
#
# 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
# from_server_data = phone.recv(1024) # 最多接受1024字节
# print(f'from_server_data.decode("gbk")')
#
# phone.close()
5.粘包现象
5.1服务端(server)
# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
# import socket
# import subprocess
# 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) # 最多接受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()
# print(f'总字节数:len(result)')
# conn.send(result)
# except ConnectionResetError:
# print('客户端链接中断了')
# break
# conn.close()
# phone.close()
# s1 = '太白jx'
# # print(len(s1))
# b1 = s1.encode('utf-8')
# # print(b1)
# print(len(b1))
'''
客户端 服务端
第一次: ipconfig 317字节
300个字节 17个字节
客户端 服务端
第二次: dir 376字节
17字节 376字节
'''
# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(1024) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
# conn.close()
# phone.close()
# 展示一些收发的问题。
5.2客户端(client)
# import socket
#
# 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
# from_server_data = phone.recv(300) # 最多接受1024字节
# # print(f'from_server_data.decode("gbk")')
# print(len(from_server_data))
#
# phone.close()
# 2. 连续短暂的send多次(数据量很小),你的数据会统一发送出去.
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'he')
# phone.send(b'll')
# phone.send(b'o')
#
#
# phone.close()
# Nigle算法
5.3展示收发问题的服务端(server)
# 发多次收一次
# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(1024) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(1024) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
# conn.close()
# phone.close()
# 发一次收多次
# import socket
#
# phone = socket.socket()
#
# phone.bind(('127.0.0.1',8848))
#
# phone.listen(5)
#
#
# conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
#
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# from_client_data = conn.recv(3) # 最多接受1024字节
# print(f'来自客户端addr消息:from_client_data.decode("utf-8")')
#
# conn.close()
# phone.close()
5.4 展示收发问题的客户端(client)
# 发多次收一次
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'he')
# phone.send(b'llo')
#
#
# phone.close()
# Nigle算法
# 发一次收多次
# import socket
#
# phone = socket.socket()
#
# phone.connect(('127.0.0.1',8848))
#
#
# phone.send(b'hello world')
#
#
# phone.close()
6.如何解决粘包现象
解决粘包现象的思路:
服务端发一次数据 10000字节,
客户端接收数据时,循环接收,每次(至多)接收1024个字节,直至将所有的字节全部接收完毕.将接收的数据拼接在一起,最后解码.
1. 遇到的问题: recv的次数无法确定.
你发送总具体数据之前,先给我发一个总数据的长度:5000个字节。然后在发送总数据。
客户端: 先接收一个长度。 5000个字节。
然后我再循环recv 控制循环的条件就是只要你接受的数据< 5000 一直接收。
2. 遇到的问题: 总数据的长度转化成的字节数不固定
服务端:
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类型才可以发送
387 ---- > str(387) '387' ---->bytes b'387' 长度 3bytes
4185 ----> str(4185) '4185' ---->bytes b'4185' 长度 4bytes
18000------------------------------------------------------> 长度 5bytes
我们要解决:
将不固定长度的int类型转化成固定长度的bytes并且还可以翻转回来。
struct模块
5.low版解决粘包现象
5.1服务端
# 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 = 'lagfdkjglkhjklh'
# b1 = s1.encode('utf-8')
# print(b1)
# print(len(b1))
5.2客户端(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()
以上是关于Python网络编程03/ low版解决粘包问题的主要内容,如果未能解决你的问题,请参考以下文章