粘包问题及解决方案

摘要:
Stderr=子流程。PIPE)#将结果赋给名为result=obj的结果变量。标准输出。read()+对象。标准错误。read()print(len(result))print(result.decode('gbk'))#Windows系统下的默认代码gbk#将结果返回到客户端conn.send(result)异常:

一、粘包问题
问题1: 无法确认对方发送过来数据的大小。

‘client.py'

import socket

client = socket.socket()

client.connect(
('127.0.0.1', 9000)
)

while True:

cmd = input('客户端输入的内容: ')

client.send(cmd.encode('utf-8'))

data = client.recv(19190)
print(len(data))
print(data.decode('gbk'))
‘server.py'

import socket
import subprocess

server = socket.socket()
server.bind(('127.0.0.1',9000))
server.listen(5)

while True:
conn,addr = server.accept()
print(addr)
while True:
try:
cmd = conn.recv(10)
if len(cmd) == 0:
continue
cmd = cmd.decode('utf-8') #utf8
if cmd == 'q':
break
#调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
obj = subprocess.Popen(
#cmd接受的是解码后的字符串
cmd,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
#结果交给result变量名
result = obj.stdout.read()+obj.stderr.read()
print(len(result))
print(result.decode('gbk')) #windows系统下默认编码gbk
#将结果返回给客户端
conn.send(result)

except Exception as e:
print(e)
break
conn.close()

问题2: 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送。

‘client.py'

import socket

client = socket.socket()

client.connect(
('127.0.0.1', 9000)
)

client.send(b'hello')
client.send(b'hello')
client.send(b'hello')
‘server.py'

import socket

server = socket.socket()

server.bind(
('127.0.0.1', 9000)
)

server.listen(5)

conn, addr = server.accept()

data = conn.recv(5)
print(data) # b'hello'

data = conn.recv(1024)
print(data) # b'hello'

data = conn.recv(1024)
print(data) # b'hello'
二、粘包问题的解决方案:
粘包问题的解决方案: 确认对方数据的大小。

这里需要用 struct模块

struct是什么?
是一个python内置的模块,它可以将固定长度的数据,打包成固定格式的长度。
固定格式:如 “ i ” 模式
i : 4

struct作用:
可以将真实数据,做成一个固定长度的报头,客户端发送给服务器,服务器可以接受报头,然后对报头进行解包,获取真实数据的长度,进行接收即可

import struct

data = b'1111111111111111'
print(len(data)) #16

#打包制作报头
header = struct.pack('i',len(data))
print(header) #b'x10x00x00x00'
print(len(header)) #4

#解包获取真实数据长度 --->得到一个元组,元组中第一个值是真实数据的长度
res = struct.unpack('i',header)[0]
print(res) #16

无论哪一端先发送数据

客户端
- 1) 先制作报头,并发送 (struct)
- 2) 发送真实数据

服务端:
- 1) 接收报头,并解包获取 真实数据长度
- 2) 根据真实数据长度 接收真实数据
recv(真实数据长度)

‘client.py'

在这里插入代码片import socket
import struct

client = socket.socket()

client.connect(
('127.0.0.1', 9000)
)

while True:

cmd = input('客户端输入的内容: ')
cmd_bytes = cmd.encode('utf-8')

#做一个报头
header = struct.pack('i',len(cmd_bytes))
print(len(header))
client.send(header)

#待服务端确认长度后,发送真实数据长度
client.send(cmd_bytes)
#接受服务端的报头
headers = client.recv(4)

#解包,接受服务器返回的真实数据的长度
data_len = struct.unpack('i',headers)[0]
result = client.recv(data_len)

print('接受服务器返回的真实数据的长度',len(result))
print(result.decode('gbk'))

# 问题2:
# import socket
#
# client = socket.socket()
#
# client.connect(
# ('127.0.0.1', 9000)
# )
#
# client.send(b'hello')
# client.send(b'hello')
# client.send(b'hello')

‘server.py'

import socket
import subprocess
import struct

server = socket.socket()
server.bind(('127.0.0.1',9000))
server.listen(5)

while True:
conn,addr = server.accept()
print(addr)
while True:
try:
#获取客户端传过来的报头
header = conn.recv(10)
#解包获取真实数据的长度
data_len = struct.unpack('i',header)[0]

#准备接受真实数据
cmd = conn.recv(data_len)

if len(cmd) == 0:
continue
cmd = cmd.decode('utf-8') #utf8
if cmd == 'q':
break
#调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
obj = subprocess.Popen(
#cmd接受的是解码后的字符串
cmd,shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
#结果交给result变量名
result = obj.stdout.read()+obj.stderr.read()
print('发送给服务端返回的真实数据的长度', len(result))
# print(result.decode('gbk')) #windows系统下默认编码gbk

# 做一个报头,返回给客户端
header = struct.pack('i', len(result))
print(len(header))
conn.send(header)

#将结果返回给客户端
conn.send(result)


except Exception as e:
print(e)
break
conn.close(
原文链接:https://blog.csdn.net/weixin_45816565/article/details/103410678

免责声明:文章转载自《粘包问题及解决方案》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇UML的九种模型图AngularJS 指令之 ng-hide/ng-show下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

时序数据库InfluxDB的基本语法

一 了解InfluxDB的必要性 时序数据库主要存放的数据 Time series data is a series of data points each associated with a specific time. Examples include: Server performance metrics Financial averages ov...

iptables 简单学习

目录 预热:网络基础 一、查看路由表信息 二、IP路由选择实验 1.route相关命令 2.实验需求 3.具体步骤 Iptables 防火墙 课程目标 一、防火墙介绍 二、Iptables防火墙介绍 三、Iptables防火墙结构 四、防火墙工作原理(数据包流向) 五、Iptables 基本语法 1>Filter表 1.示例1:...

torch 深度学习(5)

torch 深度学习(5) mnist torch siamese deep-learning 这篇文章主要是想使用torch学习并理解如何构建siamese network。 siamese network的结构如下: 1486455020988.jpg 使用的数据集:mnist 手写数据集 实验目的:通过孪生网络使得同一类的尽可能的靠近...

vue中的锚链接跳转问题

一、在vue中的锚链接和普通的html不同 关于vue中的锚链接可以参考vue 中的  scrollBehavior 滚动行为。 在router.js中  const router = new VueRouter({     routes,       mode: 'history',       scrollBehavior(to, fro...

mysql如何存储过程返回记录的更新条数

#ROW_COUNT()返回被前面语句升级的、插入的或删除的行数。 #这个行数和 mysql 客户端显示的行数及 mysql_affected_rows() C API 函数返回的值相同。 use test; create table t(id int,NAME varchar(200),addr varchar(200)); INSERT INTO t...

执行计划--Adhoc和Prepare

在和SQLPass讨论adhoc和Prepare时,有各自不同的观点,我来发表下我的理解,不对之处,敬请指出! Adhoc(即席查询):没有参数化的查询计划会被标记为adhoc,adhoc不能理解为该执行计划不会被重用。 Prepared(预定义):查询中使用到参数的执行计划会被标记为Prepared. 在后续测试中,每次测试之前需要清除执行计划: --清...