一,单线程 - shell交互

def chan_recv(chan):
data = chan.recv(1024) # 收1024数据
sys.stdout.write(data.decode()) # 输出
sys.stdout.flush() if __name__ == '__main__':
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('10.10.10.2', port=22, username='cisco', password='cisco', timeout=3) # 3秒超时
channel = ssh.invoke_shell() chan_recv(channel) # 开始前先收一下数据
while True: # 监听输入
d = input()
if d == 'quit': # 如果输入quit,就退出
break
channel.send(d + '\n')
chan_recv(channel) channel.close()
ssh.close()

问题:接收数据时的不规则性,chan.recv(1024)每次只收1024数据:

1,如果发送方的数据大于1024,就导致一次就取不完,需要分多次取

2,粘包问题: 即使发送方数据小于1024,但是如果去缓存取数据的时候数据还没到达,也会导致一次取不完;而且也可能会取到下一次命令的返回数据,即如果交互多次,此时输入命令和拿到的结果无法一一对应,

以上代码在执行时,获取不到预期结果时,多敲几个回车就会出结果

解决方法:

1,第二个粘包问题可以通过sleep粗暴解决

2,如果想把两个问题同时解决,主要有三个方法:

  a)发送实际数据前,server端先发数据大小,client端持续接收,并且最后一次不收1024,而收实际大小,但是像paramiko这种server端无法改造的不适用(老男孩python socket编程就是这种解决思路)

  b)明确结尾标示符,即做回显判断,每输入一条命令,都接收到“结尾标示符”为止,参考“python paramiko自动登录网络设备抓取配置信息”

  c)双线程,主线程做输入,子线程持续不断接收

二,双线程 - shell交互

def chan_recv(chan):
while True:
data = chan.recv(1024) # data是收到的数据,每次收1024
if not data: # 客户端输入了断开socket的命令(例如exit),会导致子线程循环结束
break
sys.stdout.write(data.decode())
sys.stdout.flush() if __name__ == '__main__':
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('10.10.10.2', port=22, username='cisco', password='cisco', timeout=3) # 3秒超时
channel = ssh.invoke_shell() # 这里如果不设置daemon进程,即使主线程关闭退出,子线程也不会结束
writer = threading.Thread(target=chan_recv, args=(channel,), daemon=True)
writer.start()
while True:
d = input()
if d == 'quit':
break
channel.send(d + '\n')
# writer.join() # 这里不用join了,线程已经设置为守护线程,主线程结束就会自动关闭守护线程了 channel.close()
ssh.close()

备注:

1,可以看到相比单线程-shell交互,双线程版的子线程可以通过while循环接收server,也就不用去关心粘包、一次收1024能不能收完这类问题了

2,server端可能会有交互要求输入,例如server端可能进行了分屏,返回--More--,要求输入空格后,才继续显示,此时需要先取消分屏

3,输入一条命令,界面上会显示两遍,第一遍是自己客户端输入的,第二遍是server端的回显

三,双线程 - 执行预先定义的命令

本例是对“python paramiko自动登录网络设备抓取配置信息”的改进,无需事先确定回显内容

import paramiko
import threading class MyThread(threading.Thread): def __init__(self, func, args=()):
super(MyThread, self).__init__()
self.func = func
self.args = args def run(self):
self.result = self.func(*self.args) def get_result(self):
try:
return self.result
except Exception:
return None def chan_recv(chan):
resp = ''
while True:
data = chan.recv(1024)
if not data:
break
resp += data.decode()
return resp def shell(commands, host, port, username, password, timeout=3):
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port=port, username=username, password=password, timeout=timeout)
channel = ssh.invoke_shell()
writer = MyThread(chan_recv, args=(channel,))
writer.start()
for cmd in commands:
channel.send(cmd)
writer.join()
channel.close()
ssh.close()
return writer.get_result() if __name__ == '__main__':
cmds = ['enable\n', 'cisco\n', 'terminal length 0\n', 'show ip int br\n',
'show run\n', 'show version\n', 'show inventory\n',
'sh cdp nei\n', 'conf t\n', 'router ospf 110\n',
'network 10.10.10.2 0.0.0.0 area 0\n',
'end\n', 'exit\n']
res = shell(cmds, '10.10.10.2', '22', 'cisco', 'cisco')
print(res) 

执行时,有时会遇到EOFerror报错,需要在接收数据的时候做下改造:

    while True:
try:
data = channel.recv(1024)
if not data:
break
resp += data.decode()
except EOFError:
pass

  

四,单线程 - 执行预先定义的命令

其实执行预先定义的命令,不存在交互,无需另起线程,可以用串行方式,先把命令发过去,再不停接收数据即可。

import paramiko
import os def shell(commands, host, port, username, password, timeout=3):
ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port=port, username=username, password=password, timeout=timeout)
channel = ssh.invoke_shell() for cmd in commands:
channel.send(cmd) resp = ''
while True:
try:
data = channel.recv(1024) # 如果对方没有发东西过来,就会一直阻塞在recv
if not data: # 如果发过来是b'',退出
break
resp += data.decode()
except EOFError: # 如果发过来了其他终止符,导致EOF
pass # 只能用pass,不能用break channel.close()
ssh.close()
return resp if __name__ == '__main__':
cmds = ['enable\n', 'cisco\n', 'terminal length 0\n', 'show ip int br\n',
'show run\n', 'show version\n', 'show inventory\n',
'sh cdp nei\n', 'conf t\n', 'router ospf 110\n',
'network 10.10.10.2 0.0.0.0 area 0\n',
'end\n', 'exit\n']
res = shell(cmds, '10.10.10.2', '22', 'cisco', 'cisco')
print(res)

  

总结:

结束的判断主要有三种方法:(参考https://www.cnblogs.com/litaozijin/p/6624029.html)

1,服务器端预先发送数据长度,每次接收时判断(老男孩python课件中socket编程也是这个方法)

2,结尾标示符

3,终止socket

收数据时的方法有两种:

1,单线程

2,双线程:主线程发送命令,子线程负责持续接收

python paramiko登陆设备的更多相关文章

  1. Python Paramiko模块与MySQL数据库操作

    Paramiko模块批量管理:通过调用ssh协议进行远程机器的批量命令执行. 要使用paramiko模块那就必须先安装这个第三方模块,仅需要在本地上安装相应的软件(python以及PyCrypto), ...

  2. python paramiko模拟ssh登录,实现sftp上传或者下载文件

    Python Paramiko模块的安装与使用详解 paramiko是短链接,不是持续链接,只能执行你设定的shell命令,可以加分号执行两次命令. http://www.111cn.net/phpe ...

  3. Python模拟登陆新浪微博

    上篇介绍了新浪微博的登陆过程,这节使用Python编写一个模拟登陆的程序.讲解与程序如下: 1.主函数(WeiboMain.py): import urllib2 import cookielib i ...

  4. python+paramiko库+svn写的自动化部署脚本

    第一篇博文 直接开门见山的说了. 这是件什么事?:每次部署都是复制本地的文件粘贴到服务器端,因为路径复杂,所以费时且手工容易出漏洞. 一直在想有什么办法可以解决这种,因为以前在微软的一个牛人同事做过一 ...

  5. 使用python检测一个设备是否ping的通

    使用python检测一个设备是否ping的通 一,subprocess以及常用的封装函数 运行python的时候,我们都是在创建并运行一个进程.像Linux进程那样,一个进程可以fork一个子进程,并 ...

  6. Python模拟登陆万能法-微博|知乎

    Python模拟登陆让不少人伤透脑筋,今天奉上一种万能登陆方法.你无须精通HTML,甚至也无须精通Python,但却能让你成功的进行模拟登陆.本文讲的是登陆所有网站的一种方法,并不局限于微博与知乎,仅 ...

  7. Python模拟登陆TAPD

    因为在wiki中未找到需要的数据,查询也很迷,打算用python登录tapd抓取所需项目下的wiki数据,方便查找. 2018-9-30 19:12:44 几步走 模拟登录tapd 抓取wiki页左侧 ...

  8. Python模拟登陆淘宝并统计淘宝消费情况的代码实例分享

    Python模拟登陆淘宝并统计淘宝消费情况的代码实例分享 支付宝十年账单上的数字有点吓人,但它统计的项目太多,只是想看看到底单纯在淘宝上支出了多少,于是写了段脚本,统计任意时间段淘宝订单的消费情况,看 ...

  9. 使用 Python 编写登陆接口

    # 使用 Python 编写登陆接口# Create Date: 2017.10.31 Tuesday# Author: Eric Zhao# -*- coding:utf-8 -*-'''编写登陆接 ...

随机推荐

  1. 第三节MapStruct翻译--Defining a mapper

    第三节MapStruct--Defining a mapper 在这一章节你将学到如何用mapstruct和它的一些必要的操作选项来定义一个bean mapper. 3.1 Basic mapping ...

  2. 简述哲学Essay写作

    哲学类essay写作对于中国留学生来说算是比较难的作业了,它不仅有结构要求,还注重逻辑的紧密性.很多同学都不知道该怎么下手.今天小编就给同学们分享哲学essay写作的结构.同学们可以尝试按照以下方法来 ...

  3. 【Android】家庭记账本手机版开发报告一

    一.说在前面 昨天 学习了数据库的一些简单操作 今天 使用数据库,完成对记账本的账单记录的增删 问题 没有 二.数据库 1.账单表的结构 (注 id:账单的唯一标识,uid:记录账单的用户的id,co ...

  4. java 学生信息管理

    题目: 一.测试要求:      1.按照测试内容要求完成程序的设计与编程:      2.将最终结果的源文件(.java)文件上传到以班级为单位,保存源程序.      3.建立学号姓名文件夹,如: ...

  5. POJ 1284:Primitive Roots 求原根的数量

    Primitive Roots Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 3381   Accepted: 1980 D ...

  6. 如何生成 SSH keys, 并在 Github 或 Gitlab 等上添加密钥

    1 打开 Git Bash $ 2 输入 dir, 确认当前文件夹,并切换到想存密钥文件即pub文件的路径 $ dir 3 生成 密钥命令 ssh-keygen -t rsa -C "{ y ...

  7. MYSQL 数据库名、表名、字段名查询

    //查询所有表的所有字段: select * from information_schema.columns where table_name='sys_users' 效果: //查询指定表的所有字段 ...

  8. bash: java: command not found

    [root@izm5eab8t820b79js38tbxz ~]# java -version -bash: java: command not found 出现上面问题,解决方法: [root@iz ...

  9. electron app弹出默认对话框后页面失去焦点问题

    最近再做electron app程序的做删除数据操作的时候遇到一个诡异的bug,页面点击删除按钮后,弹出确认对话框后,页面失去焦点,文本框无法点击输入任何参数,但是使用浏览器操作正常,最后确定是ele ...

  10. VUE.js入门学习(3)-深入理解VUE组建

    1.使用组件的细节点 (1)is="模版名" (2)在子组建定义data的时候,data必须是一个函数,而不能是一个对象,每个子组建都有自己的数据存储.之间不会相互影响. (3)操 ...