首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Python 远程控制模块 paramiko 问题解决记录

Python 远程控制模块 paramiko 问题解决记录

原创
作者头像
张戈
修改于 2017-08-07 07:39:36
修改于 2017-08-07 07:39:36
17.8K01
代码可运行
举报
文章被收录于专栏:张戈的专栏张戈的专栏
运行总次数:1
代码可运行

导语 生产环境中使用paramiko作SSH远程控制时,发现会有部分机器报Error reading SSH protocol banner错误,尝试使用ssh命令连接此机器,只是卡半天,最终还是能够正常登陆。

最近一直做运维发布平台,底层命令行、文件通道主要基于paramiko模块,使用过程中遇到各种各样的问题,本文主要用于收集问题及解决记录,以备后续使用。

一、Error reading SSH protocol banner连接错误

这个关键词,在百度、谷歌一搜一大把的提问,也有少部分给出了解决方案,但是最终都无法解决,我经过不断尝试和解读paramiko源码,终于搞定了这个问题,在此记录分享下。

1、具体报错信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
  File "build/bdist.linux-x86_64/egg/paramiko/client.py", line 307, in connect
  File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 465, in start_client
paramiko.SSHException: Error reading SSH protocol banner

2、解决办法:

重新下载paramiko插件源码,解压后,编辑安装目录下的transport.py文件:

vim build/lib/paramiko/transport.py

搜索 self.banner_timeout 关键词,并将其参数改大即可,比如改为300s:

self.banner_timeout = 300

最后,重装paramiko即可。

3、下面的曲折、啰嗦的解决过程,不喜请跳过:

在谷歌搜到一个老外相关提问,虽然他说的是pysftp,其实也是基于paramiko:

https://stackoverflow.com/questions/34288526/pysft-paramiko-grequests-error-reading-ssh-protocol-banner/44493465#44493465

他最后给出了他的解决方案:

UPDATE: It seems the problem is caused by importing the package grequests. If I do not import grequests, pysftp works as expected. The issue was raised before but has not been solved

意思是,在paramiko使用前,先import grequests,就能解决问题。我照做之后,发现对手头的现网环境无效,可能错误产生的原因不一样。

但是,我从老外的问题描述过程中,找到了解决方法,他是这样说的:

I have already tried changing the banner timeout from 15 seconds to 60 secs in the transport.py, but it did not solve the problem.

我看到有个timeout和transport.py,就想到现网那些报Error reading SSH protocol banner错误的机器也是非常卡,而且目测了下发起paramiko连接到报错的时间,基本是相同的。

于是系统中搜索,并找到了transport.py这个文件:

/usr/lib/python2.7/site-packages/paramiko/transport.py

并搜了下banner,发现果然有一个参数设置,而且和目测的超时基本一致!

于是,顺手修改成300S,并重新测试发现没任何效果,依然15S超时。接着打断点、直接移走这个问题,问题依旧!!看来这个文件不会被引用。。。

回到最初的报错信息,发现里面显示的是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
build/bdist.linux-x86_64/egg/paramiko/transport.py

而系统里面搜不到这个问题,最后醍醐灌顶,发觉Python模块编译后,基本是以egg文件保存的,看来 必须修改源码才行了。

于是cd到paramiko的源码目录,执行搜索,找到2各transport.py文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[root@localhost:/data/software/paramiko-1.9]# find . -name transport.py
./paramiko/transport.py
./build/lib/paramiko/transport.py

尝试将文件中的 self.banner_timeout 值改成300,重新安装paramiko,结果一次性测试成功!

二、paramiko远程执行后台脚本“阻塞”问题

我写的远程命令通道上线之后,发现在远程脚本中后台再执行另一个脚本,通道会一直等待后台脚本执行完成才会返回,有时甚至会僵死。

1、复现过程如下:

①、编写测试脚本

脚本1:test.sh

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/bash
sleep 30
echo test end
exit 0

脚本2:run.sh

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#!/bin/bash
bash /tmp/test.sh &
echo run ok!
exit 0

脚本3:test.py

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import paramiko
client = paramiko.SSHClient()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname='192.168.1.10', port=22, username='root', password='123456', timeout=300,allow_agent=False,look_for_keys=False)                          
stdin,stdout,stderr=client.exec_command("bash /tmp/run.sh")                        
result_info = ""
for line in  stdout.readlines():
    result_info += line
print result_info

将test.sh和run.sh传到远程服务器上,比如放到192.168.1.10:/tmp/下。

②、发起远程执行

在本地执行 python test.py,会发现整个脚本不会立即打印run ok,而是等30s之后才打印包括test.sh的所有输出信息。

2、解决办法

将远程脚本的标准输出stdout重定向到错误输出stderr即可,test.py 修改如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import paramiko
client = paramiko.SSHClient()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname='192.168.1.10', port=22, username='root', password='123456', timeout=300,allow_agent=False,look_for_keys=False)                          
stdin,stdout,stderr=client.exec_command("bash /tmp/run.sh 1>&2")                 
result_info = ""
for line in  stderr.readlines():
    result_info += line
print result_info

现在执行,就能立即得到结果了。其实原因很简单,因为bash /tmp/test.sh & 虽然是后台执行,但是依然会产生标准输出,一旦产生标准输出,paramiko就会认为命令还未执行完成,且stdout的buffer大于stderr,因此产生等待问题。

这里只要将脚本执行的标准输出重定向到错误输出(1>&2),然后paramiko就可以使用stderr快速读取远程打屏信息了。

三、This operation would block forever 报错解决

这次扩容一个基于pramiko的自动化apiserver,结果发现在新环境执行远程命令或文件传输时,抛了如下报错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2017-08-04 12:38:31,243 [ERROR] Exception: Error reading SSH protocol banner('This operation would block forever', <Hub at 0x38b02d0 epoll pending=0 ref=0 fileno=28>)
2017-08-04 12:38:31,244 [ERROR] Traceback (most recent call last):
2017-08-04 12:38:31,244 [ERROR]   File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 1555, in run
2017-08-04 12:38:31,245 [ERROR]     self._check_banner()
2017-08-04 12:38:31,245 [ERROR]   File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 1681, in _check_banner
2017-08-04 12:38:31,245 [ERROR]     raise SSHException('Error reading SSH protocol banner' + str(x))
2017-08-04 12:38:31,245 [ERROR] SSHException: Error reading SSH protocol banner('This operation would block forever', <Hub at 0x38b02d0 epoll pending=0 ref=0 fileno=28>)
2017-08-04 12:38:31,245 [ERROR] 
2017-08-04 12:38:31,247 [INFO] Error reading SSH protocol banner('This operation would block forever', <Hub at 0x38b02d0 epoll pending=0 ref=0 fileno=28>)

总以为是python组件安装有问题,反反复复检查,最终发现居然是多装了一个插件导致的!

解决办法:

删除已经安装 greenlet插件即可,具体原因见后文:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
rm -r /usr/local/python2.7.5/lib/python2.7/site-packages/greenlet*

下面是"艰难险阻"的解决过程,不喜勿看:

1、看到报错,作为懒人第一时间就搜了下 【This operation would block forever', <Hub】这个关键词,发现没能get到解决方案。

2、按照经验,我先找到图中 _check_banner 函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def _check_banner(self):
        # this is slow, but we only have to do it once
        for i in range(100):
            # give them 15 seconds for the first line, then just 2 seconds
            # each additional line.  (some sites have very high latency.)
            if i == 0:
                timeout = self.banner_timeout
            else:
                timeout = 2
            try:
                buf = self.packetizer.readline(timeout)
            except ProxyCommandFailure:
                raise
            except Exception, x:
                raise SSHException('Error reading SSH protocol banner' + str(x))
            if buf[:4] == 'SSH-':
                break
            self._log(DEBUG, 'Banner: ' + buf)
        if buf[:4] != 'SSH-':
            raise SSHException('Indecipherable protocol version "' + buf + '"')
        # save this server version string for later
        self.remote_version = buf
        # pull off any attached comment
        comment = ''
        i = string.find(buf, ' ')
        if i >= 0:
            comment = buf[i+1:]
            buf = buf[:i]
        # parse out version string and make sure it matches
        segs = buf.split('-', 2)
        if len(segs) < 3:
            raise SSHException('Invalid SSH banner')
        version = segs[1]
        client = segs[2]
        if version != '1.99' and version != '2.0':
            raise SSHException('Incompatible version (%s instead of 2.0)' % (version,))
        self._log(INFO, 'Connected (version %s, client %s)' % (version, client))

3、很明显这个异常由 buf = self.packetizer.readline(timeout) 语句抛出,我印象中的粗暴定位方法就是不使用try,直接将此语句执行看看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    def _check_banner(self):
        # this is slow, but we only have to do it once
        for i in range(100):
            # give them 15 seconds for the first line, then just 2 seconds
            # each additional line.  (some sites have very high latency.)
            if i == 0:
                timeout = self.banner_timeout
            else:
                timeout = 2
            buf = self.packetizer.readline(timeout)  # 不使用try,看看是从哪出来的异常
            try:
                buf = self.packetizer.readline(timeout)
            except ProxyCommandFailure:
                raise
            ....

结果报错信息就更加具体了,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2017-08-04 13:23:26,085 [ERROR] Unknown exception: ('This operation would block forever', <Hub at 0x20390f0 epoll pending=0 ref=0 fileno=27>)
2017-08-04 13:23:26,087 [ERROR] Traceback (most recent call last):
2017-08-04 13:23:26,088 [ERROR]   File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 1555, in run
2017-08-04 13:23:26,088 [ERROR]     self._check_banner()
2017-08-04 13:23:26,088 [ERROR]   File "build/bdist.linux-x86_64/egg/paramiko/transport.py", line 1676, in _check_banner
2017-08-04 13:23:26,088 [ERROR]     buf = self.packetizer.readline(timeout)
2017-08-04 13:23:26,088 [ERROR]   File "build/bdist.linux-x86_64/egg/paramiko/packet.py", line 280, in readline
2017-08-04 13:23:26,088 [ERROR]     buf += self._read_timeout(timeout)
2017-08-04 13:23:26,088 [ERROR]   File "build/bdist.linux-x86_64/egg/paramiko/packet.py", line 468, in _read_timeout
2017-08-04 13:23:26,089 [ERROR]     x = self.__socket.recv(128)
2017-08-04 13:23:26,089 [ERROR]   File "/usr/local/python2.7.5/lib/python2.7/site-packages/gevent-1.1.2-py2.7-linux-x86_64.egg/gevent/_socket2.py", line 280, in recv
2017-08-04 13:23:26,089 [ERROR]     self._wait(self._read_event)
2017-08-04 13:23:26,089 [ERROR]   File "/usr/local/python2.7.5/lib/python2.7/site-packages/gevent-1.1.2-py2.7-linux-x86_64.egg/gevent/_socket2.py", line 179, in _wait
2017-08-04 13:23:26,089 [ERROR]     self.hub.wait(watcher)
2017-08-04 13:23:26,089 [ERROR]   File "/usr/local/python2.7.5/lib/python2.7/site-packages/gevent-1.1.2-py2.7-linux-x86_64.egg/gevent/hub.py", line 630, in wait
2017-08-04 13:23:26,089 [ERROR]     result = waiter.get()
2017-08-04 13:23:26,089 [ERROR]   File "/usr/local/python2.7.5/lib/python2.7/site-packages/gevent-1.1.2-py2.7-linux-x86_64.egg/gevent/hub.py", line 878, in get
2017-08-04 13:23:26,090 [ERROR]     return self.hub.switch()
2017-08-04 13:23:26,090 [ERROR]   File "/usr/local/python2.7.5/lib/python2.7/site-packages/gevent-1.1.2-py2.7-linux-x86_64.egg/gevent/hub.py", line 609, in switch
2017-08-04 13:23:26,090 [ERROR]     return greenlet.switch(self)
2017-08-04 13:23:26,090 [ERROR] LoopExit: ('This operation would block forever', <Hub at 0x20390f0 epoll pending=0 ref=0 fileno=27>)
2017-08-04 13:23:26,090 [ERROR] 
2017-08-04 13:23:26,093 [INFO] ('This operation would block forever', <Hub at 0x20390f0 epoll pending=0 ref=0 fileno=27>)

这次基本就定位到了gevent和greenlet这个真凶了!本以为是我的apiserver调用了gevent,结果定位了半天,确定并没有使用。而且印象中paramiko这个插件也没用到gevent,可这异常是怎么来的?

直到我再次在谷歌搜索【LoopExit: ('This operation would block forever', <Hub at】关键词,找到一个博客文章:http://www.hongquan.me/?p=178,总算知道是什么原因了!

具体原因:主要是因为 greenlet 里面有个run函数,覆盖了 paramiko 的transport.py 里面的同名函数,导致paramiko执行_check_banner时,实际调用了greenlet的run函数,因此报错!再次醉了!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
如何把ASP.NET Core WebApi打造成Mcp Server
MCP (Model Context Protocol)即模型上下文协议目前不要太火爆了,关于它是什么相信大家已经很熟悉了。目前主流的AI开发框架和AI工具都支持集成MCP,这也正是它的意义所在。毕竟作为一个标准的协议,当然是更多的生态接入进来才会有意义。使用MCP我们可以把Tools调用标准化,这意味着我们可以忽略语言、框架快速把工具融合到不同的模型中去。现在,如何把现有的业务逻辑快速的接入到模型中,成为模型落地很关键的一步,今天我们就借助微软的Semantic Kernel和Microsoft.Extensions.AI框架,通过简单的示例展示,如何把现有的ASP NET Core WebApi转换成MCP Server。
郑子铭
2025/06/11
1250
如何把ASP.NET Core WebApi打造成Mcp Server
MCP协议从原理到开发:一文读懂大模型交互的标准化革命!
本文系统介绍了MCP协议在大模型交互标准化中的创新应用,通过技术解析+实践案例的方式,阐述了MCP协议的架构设计、开发实现原理及实际应用效果。重点探讨了MCP如何解决AI工具调用碎片化问题,并通过企业微信机器人开发实例展示MCP服务端/客户端开发全流程,干货满满点赞收藏!
腾讯云开发者
2025/04/18
3.4K1
MCP协议从原理到开发:一文读懂大模型交互的标准化革命!
工良出品 | 长文讲解 MCP 和案例实战
示例项目地址:https://github.com/whuanle/mcpdemo
郑子铭
2025/06/07
4920
工良出品 | 长文讲解 MCP 和案例实战
工良出品 | 长文讲解 MCP 和案例实战
示例项目地址:https://github.com/whuanle/mcpdemo
痴者工良
2025/04/22
1.8K1
工良出品 | 长文讲解 MCP 和案例实战
Java 实现 MCP Server 以及常用 MCP 服务分享
MCP 前段时间在 AI 领域 引发了 广泛关注,特别是在 各大海内外技术社区 中,大家热烈讨论,热度非常高,本文将带领大家使用 java 语言实现一个 mcp,揭开 mcp 这神秘的面纱,本文最后也推荐给大家一些常用的 MCP 服务,开箱即用 0 成本,希望对大家有一定的帮助。
Lcry
2025/04/24
5.8K6
Java 实现 MCP Server 以及常用 MCP 服务分享
MCP(Model Context Protocol)好比大模型外挂!
最近Anthropic主导发布了MCP(Model Context Protocol,模型上下文协议)后,着实真真火了一把。熟悉AI大模型的人对Anthropic应该不会陌生,Claude 3.5 Sonnet模型就是他们发布的,包括现在的最强编程AI模型 3.7 Sonnet。今天我们来刨析下什么是MCP,AI大模型下,需要MCP吗?
有一只柴犬
2025/03/23
8722
MCP(Model Context Protocol)好比大模型外挂!
创建图片生成MCP Server
正如前文提到的,我仍然需要一个图片生成的 MCP Server。参考 MCP 快速入门和 Gemini 文档,我发现实现起来应该不难。
云云众生s
2025/03/20
6220
构建基于 SSE 协议通信的 MCP Server 和 Client
在之前的系列教程中,我们编写的 MCP 服务器与 MCP 客户端是通过 stdio(Standard Input/Output,标准输入输出)来进行交互的。客户端通过启动服务器子进程,并利用标准输入(stdin)和标准输出(stdout)建立双向通信。这种模式导致 MCP 客户端与服务器之间存在强耦合,且每个服务器进程只能与启动它的客户端通信(1:1 的关系)。
Se7en258
2025/05/21
7030
构建基于 SSE 协议通信的 MCP Server 和 Client
在浏览器使用 MCP,纯边缘函数实现 MCP Client & Server
下面是一个在线示例:https://mcp-on-edge.edgeone.site/?from=eo
EdgeOne 小助手
2025/05/09
8600
必看!SpringAI轻松构建MCP Client-Server架构
MCP 这个概念相信大家已经听了无数次了,但不同人会有不同的解释,你可能也是听得云里雾里的。
磊哥
2025/03/30
8881
必看!SpringAI轻松构建MCP Client-Server架构
快速上手:实现你的第一个 MCP Client
在 MCP Server 开发实战:无缝对接 LLM 和 Elasticsearch 一文中,我们详细介绍了如何利用 MCP Python SDK 编写一个 Elasticsearch MCP 服务器,并通过 Claude Desktop 作为 MCP 客户端进行交互。本文将进一步介绍如何使用 MCP Python SDK 编写一个 MCP 客户端,以便更加灵活地与 MCP 服务器进行通信和集成。本文的完整代码可以在 Github 上找到:https://github.com/cr7258/hands-on-lab/tree/main/ai/claude/mcp/client/elasticsearch-mcp-client-example
Se7en258
2025/05/21
7340
快速上手:实现你的第一个 MCP Client
一文详解模型上下文协议(MCP):打通大模型与业务场景的关键
暂且抛开MCP,这23年的时候开始搭建AI Agent智能体,对第三方插件API进行交互的时候,我就开始设想能不能自己做一个通用代码协议框架,以后AI团队统一用这个协议,方便大家code review,更好协作。AI要触及到业务就必然逃不过与业务端接口或是数据进行联通,但是之前都没有标准的交互协议,需要理解各个三方接口和协议是比较费时的事情,但如果有类似像Java工程开发标准,那么我们就很方便开发第三方接口了,不会存在那么多不同开发形态的代码,方便维护。MCP协议出现之后发现大家都在慢慢融入到开源协议框架中,故而再对一些不了解MCP的朋友详细解述这一框架协议,以后必然是以开源协议为主导的代码生态。
fanstuck
2025/03/27
3K2
一文详解模型上下文协议(MCP):打通大模型与业务场景的关键
一文掌握 MCP 上下文协议:从理论到实践
应用能够安全、灵活地访问和操作本地及远程数据资源,提升模型的功能性和可扩展性。 ​
陈明勇
2025/04/02
4.1K8
一文掌握 MCP 上下文协议:从理论到实践
机器学习|MCP(Model Context Protocol)实战
MCP(Model Context Protocol,模型上下文协议) ,2024年11月底,由 Anthropic 推出的一种开放标准,旨在统一大型语言模型(LLM)与外部数据源和工具之间的通信协议。 官网的介绍: https://modelcontextprotocol.io/introduction
用户1904552
2025/04/13
9590
机器学习|MCP(Model Context Protocol)实战
大语言模型交互协议 MCP SDK Go-MCP 正式开源!
今天,ThinkInAI 团队(前身为 GoCN 团队)自豪地宣布,基于 Go 语言的大模型交互协议(Model Context Protocol)SDK —— Go-MCP 正式开源!
深度学习与Python
2025/04/10
1.8K0
大语言模型交互协议 MCP SDK Go-MCP 正式开源!
【译】微软与 Anthropic 合作为 MCP 创建官方 C# SDK
    微软正在与 Anthropic 合作,为模型上下文协议(MCP)创建一个官方的 C# SDK。MCP 已经在人工智能社区得到了迅速的应用,这次合作旨在增强人工智能模型与 C#应用程序的集成。
郑子铭
2025/06/28
580
【译】微软与 Anthropic 合作为 MCP 创建官方 C# SDK
[C#] 常用工具类——文件操作类
/// <para> FilesUpload:工具方法:ASP.NET上传文件的方法</para> /// <para> FileExists:返回文件是否存在</para> /// <para> IsImgFilename:判断文件名是否为浏览器可以直接显示的图片文件名</para> /// <para> CopyFiles:复制指定目录的所有文件</para> /// <para> MoveFiles:移动指定目录的所有文件</para> /// <para> D
跟着阿笨一起玩NET
2018/09/19
2.7K0
PHP MCP 客户端 v1.0.0 发布!
PHP MCP Client是一个PHP库,用于与实现Model Context Protocol (MCP)的服务器进行交互。它为开发者提供了友好的接口,可通过不同的传输方式(stdio、http+sse)连接到单个MCP服务器,管理连接生命周期,发现服务器功能(工具、资源、提示),并执行诸如调用工具或读取资源等请求。
Tinywan
2025/05/08
2210
PHP MCP 客户端 v1.0.0 发布!
BotSharp + MCP 三步实现智能体开发
1. 简介 1.1 什么是MCP Model Context Protocol(MCP)模型上下文协议是一种标准化协议,它让大模型能够更容易地和外部的数据、工具
张善友
2025/04/05
3646
BotSharp + MCP 三步实现智能体开发
C#封装的常用文件操作代码类
这个C#类封装了我们经常能用到的文件操作方法,包括读写文件、获取文件扩展名、复制文件、追加内容到文件、删除文件、移动文件、创建目录、递归删除文件及目录、列目录、列文件等,不可多得。
用户7108768
2021/11/02
9460
推荐阅读
相关推荐
如何把ASP.NET Core WebApi打造成Mcp Server
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档