如何判断channel.recv是否接受完数据

silencereformer 2018-08-08 12:08:25
最近在写一个小程序方便监控交换机路由器防火墙的状态,遇到了一点问题。从我的电脑连接到设备的过程是:需要先通过ssh连接到堡垒机,然后从堡垒机再连到各个设备。
目前基本功能都实现了,但是我是用的接受回显的方法是这样的:

import paramiko
import time
import re

sshClient=paramiko.SSHClient()
sshClient.set_missing_host_key_policy(paramiko.AutoAddPolicy())#跳过远程连接中选择“是”的环节
cmd=r'pwd'
enterStr="\n"

try:
sshClient.connect('135.***.***.***',22,'MyUserName','MyPassword')
pass
except BaseException as ex:
print(str(ex))
print("Pres any key to exit...")
input()
exit()
pass
myChannel=sshClient.invoke_shell()

while True:
myChannel.send(cmd+enterStr)
time.sleep(0.5)
result=myChannel.recv(4096)

可以看到是通过暂停线程的方法让数据接收完的。但是这样非常不灵活,在网络状态好的时候0.5秒的暂停就显得太长了,如果要display一个资源池的相关信息可能有几万行,一次回显二十行就要停半秒,回显完十几分钟都过去了。但是有的时候网络出现拥塞,有可能出现了丢包,半秒不足以收到回包,或者收到的回包就被截断了,回显显示不全。
一开始使用的方法是

stdin, stdout, stderr = sshClient.exec_command(command)

这种方法虽然不用考虑接收数据完整性的问题但是在通过堡垒机敲命令telnet/ssh登陆设备时会出现问题,发送的命令也有很多在堡垒机上不执行。回显也不完整。
看到网友提到有三种recv完整接收数据的方法《python网络编程调用recv函数完整接收数据的三种方法》,分析了一下三种方法:因为ssh连接建立之后需要多次输入命令,所以文中的“基础数据接收法”不适合我;而回显的文字并没有固定的结束符号,而我也不能从堡垒机端对回显数据作出更改,所以“尾标识法”也不合适;第三种,因为不太清楚paramiko中socket的各种定义,也不知道有没有留出什么接口。
想知道大家对于这个问题有没有什么好的解决办法。
...全文
2191 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
思想觉醒 2021-01-28
  • 打赏
  • 举报
回复
我的解决方法是另外启动一个线程去接收数据: Thread(target=check_data_buff, args=(requet.websocket, pshell,)) def check_data_buff(websocket, pshell): while not websocket.is_closed(): time.sleep(0.01) if pshell.recv_ready(): ret = pshell.recv(102400) websocket.send(ret)
tianzhu_1 2020-03-09
  • 打赏
  • 举报
回复
我的方法是由程序执行着每次执行程序时手动定义某些命令等待时间,比如display diagnose 中间需要等待可能20秒甚至30秒,我的方法是让程序一直读,遇到关键命令则执行用户自定义的等待时间后继续读取,如果读取不到了,就是读取返回是空,则读取结束,这样最大限度保证了回显的完整性
getter2 2019-06-10
  • 打赏
  • 举报
回复 1
paramiko 的channel有个 recv_ready()的方法: 解释如下:
recv_ready()
Returns true if data is buffered and ready to be read from this channel. A False result does not mean that the channel has closed; it means you may need to wait before more data arrives.

Returns:	True if a recv call on this channel would immediately return at least one byte; False otherwise.
那么你就可以这样, 直接看信道是否 recv_ready(), 返回True 就recv(), 如果False 就等待个0.2秒再判断 但是这个recv_ready()并不能帮助我们判断接收是否结束了 至于判断是否接收完毕,我也没啥好想法, 我现在的笨办法就是看recv() 到的信息内是否出现路由器交换机的主机名(比如思科就是xxxx#, 华为华三就是 <xxxxx>), 如果出现了,说明设备回显结束了,可以break这个接收循环. 设备主机名可以用先recv()下,保证没有多余字符后,发送回车,再次recv()得到. 确实比较蠢, 如果有更合适的办法烦请告诉我,谢谢!
silencereformer 2018-08-10
  • 打赏
  • 举报
回复
引用 1 楼 oyljerry 的回复:
如果嫌时间长,可以sleep的间隔更小点。比如0.05

但是设置小了 数据就有可能没收完,导致数据被截断,有的老交换机回显config要有两秒的等待orz
oyljerry 2018-08-09
  • 打赏
  • 举报
回复
如果嫌时间长,可以sleep的间隔更小点。比如0.05

37,741

社区成员

发帖
与我相关
我的任务
社区描述
JavaScript,VBScript,AngleScript,ActionScript,Shell,Perl,Ruby,Lua,Tcl,Scala,MaxScript 等脚本语言交流。
社区管理员
  • 脚本语言(Perl/Python)社区
  • WuKongSecurity@BOB
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧