本节内容
paramiko模块
paramiko模块之SSHClient
paramiko模块之SFTPClient
paramiko模块之封装多个远程操作
堡垒机
1、实现思路
2、表结构
3、实现过程
4、window打开终端
paramiko模块
paramiko模块,基于SSH用于连接远程服务器并执行相关操作。
一、安装
pip install paramiko
paramiko模块之SSHClient
用于连接远程服务器并执行基本命令
基于用户名密码连接:
import paramiko
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='172.11.2.109', port=22, username='root', password='founder123')
# 执行命令
# stdin 交互输入的命令
# stdout 输出
# stderr 错误信息
stdin, stdout, stderr = ssh.exec_command('ls')
# 获取命令结果
result = stdout.read()
print(result.decode())
'''输出结果
anaconda-ks.cfg
install.log
install.log.syslog
installServer_nfs.sh
java
latest
redis-3.0.6
redis-3.0.6.tar.gz
sources.list
VMwareTools-9.10.5-2981885.tar.gz
vmware-tools-distrib
'''
SSHClient 内部封装 Transport
import paramiko
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', password='123')
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df')
print stdout.read()
transport.close()
SSHClient 封装 Transport
基于公钥密钥连接:
import paramiko
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='c1.salt.com', port=22, username='wupeiqi', key=private_key)
# 执行命令
stdin, stdout, stderr = ssh.exec_command('df')
# 获取命令结果
result = stdout.read()
# 关闭连接
ssh.close()
基于私钥字符进行连接
import paramiko
from io import StringIO
key_str = """-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAq7gLsqYArAFco02/55IgNg0r7NXOtEM3qXpb/dabJ5Uyky/8
NEHhFiQ7deHIRIuTW5Zb0kD6h6EBbVlUMBmwJrC2oSzySLU1w+ZNfH0PE6W6fans
H80whhuc/YgP+fjiO+VR/gFcqib8Rll5UfYzf5H8uuOnDeIXGCVgyHQSmt8if1+e
7hn1MVO1Lrm9Fco8ABI7dyv8/ZEwoSfh2C9rGYgA58LT1FkBRkOePbHD43xNfAYC
tfLvz6LErMnwdOW4sNMEWWAWv1fsTB35PAm5CazfKzmam9n5IQXhmUNcNvmaZtvP
c4f4g59mdsaWNtNaY96UjOfx83Om86gmdkKcnwIDAQABAoIBAQCnDBGFJuv8aA7A
ZkBLe+GN815JtOyye7lIS1n2I7En3oImoUWNaJEYwwJ8+LmjxMwDCtAkR0XwbvY+
c+nsKPEtkjb3sAu6I148RmwWsGncSRqUaJrljOypaW9dS+GO4Ujjz3/lw1lrxSUh
IqVc0E7kyRW8kP3QCaNBwArYteHreZFFp6XmtKMtXaEA3saJYILxaaXlYkoRi4k8
S2/K8aw3ZMR4tDCOfB4o47JaeiA/e185RK3A+mLn9xTDhTdZqTQpv17/YRPcgmwz
zu30fhVXQT/SuI0sO+bzCO4YGoEwoBX718AWhdLJFoFq1B7k2ZEzXTAtjEXQEWm6
01ndU/jhAasdfasdasdfasdfa3eraszxqwefasdfadasdffsFIfAsjQb4HdkmHuC
OeJrJOd+CYvdEeqJJNnF6AbHyYHIECkj0Qq1kEfLOEsqzd5nDbtkKBte6M1trbjl
HtJ2Yb8w6o/q/6Sbj7wf/cW3LIYEdeVCjScozVcQ9R83ea05J+QOAr4nAoGBAMaq
UzLJfLNWZ5Qosmir2oHStFlZpxspax/ln7DlWLW4wPB4YJalSVovF2Buo8hr8X65
lnPiE41M+G0Z7icEXiFyDBFDCtzx0x/RmaBokLathrFtI81UCx4gQPLaSVNMlvQA
539GsubSrO4LpHRNGg/weZ6EqQOXvHvkUkm2bDDJAoGATytFNxen6GtC0ZT3SRQM
WYfasdf3xbtuykmnluiofasd2sfmjnljkt7khghmghdasSDFGQfgaFoKfaawoYeH
C2XasVUsVviBn8kPSLSVBPX4JUfQmA6h8HsajeVahxN1U9e0nYJ0sYDQFUMTS2t8
RT57+WK/0ONwTWHdu+KnaJECgYEAid/ta8LQC3p82iNAZkpWlGDSD2yb/8rH8NQg
9tjEryFwrbMtfX9qn+8srx06B796U3OjifstjJQNmVI0qNlsJpQK8fPwVxRxbJS/
pMbNICrf3sUa4sZgDOFfkeuSlgACh4cVIozDXlR59Z8Y3CoiW0uObEgvMDIfenAj
98pl3ZkCgYEAj/UCSni0dwX4pnKNPm6LUgiS7QvIgM3H9piyt8aipQuzBi5LUKWw
DlQC4Zb73nHgdREtQYYXTu7p27Bl0Gizz1sW2eSgxFU8eTh+ucfVwOXKAXKU5SeI
+MbuBfUYQ4if2N/BXn47+/ecf3A4KgB37Le5SbLDddwCNxGlBzbpBa0=
-----END RSA PRIVATE KEY-----"""
private_key = paramiko.RSAKey(file_obj=StringIO(key_str))
transport = paramiko.Transport(('10.0.1.40', 22))
transport.connect(username='wupeiqi', pkey=private_key)
ssh = paramiko.SSHClient()
ssh._transport = transport
stdin, stdout, stderr = ssh.exec_command('df')
result = stdout.read()
transport.close()
print(result)
paramiko模块之SFTPClient
用于连接远程服务器并执行上传下载
基于用户名密码上传下载:
import paramiko
transport = paramiko.Transport(('172.11.2.109',22))
transport.connect(username='root',password='founder123')
#创建sftp对象
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
transport.close()
基于公钥密钥上传下载:
import paramiko
private_key = paramiko.RSAKey.from_private_key_file('/home/auto/.ssh/id_rsa')
transport = paramiko.Transport(('hostname', 22))
transport.connect(username='wupeiqi', pkey=private_key )
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
transport.close()
paramiko模块之封装多个远程操作
看上面的paramiko执行命令的时候有两种方法,传输文件的时候有一种方法!并且这里在执行命令的时候连接下然后关闭,传输文件的时候传输完后关闭,这样不是很好!
那么我们可以连接上,然后把执行命令和上传文件写成两个方法操作。
在远程执行命令的时候其实是很快的但是他们消耗的时间基本上都在建立连接上了,所以我们要写成连接上一次执行命令或上传文件全部都完事之后关闭。
import paramiko
class SSH:
def __init__(self,host,port,user,pwd):
self.host = host
self.port = port
self.user = user
self.pwd = pwd
#定义transport方法
#self.transport = None
def connect(self):
#通过transport连接
self.transport = paramiko.Transport((self.host,self.port))
self.transport.connect(username=self.user,password=self.pwd)
def cmd(self,cmd):
ssh = paramiko.SSHClient()
ssh._transport = self.transport
stdin,stdout,stderr = ssh.exec_command(cmd)
return stdout.read()
def download(self,server_path,local_path):
sftp = paramiko.SFTPClient.from_transport(self.transport)
sftp.get(server_path,local_path)
def upload(self,server_path,local_path):
sftp = paramiko.SFTPClient.from_transport(self.transport)
sftp.put(server_path,local_path)
def close(self):
self.transport.close()
obj = SSH('172.11.2.109',22,'root','founder123')
obj.connect()
obj.cmd('ls')
obj.cmd('df')
print(obj.cmd('df').decode())
obj.close()
'''
上面的例子中我们就连接了一次,然后用这一次连接进行命令和上传文件的管理!
不用来回的创建和关闭SSH连接
'''
import paramiko
import uuid
class SSHConnection(object):
def __init__(self, host='192.168.11.61', port=22, username='alex',pwd='alex3714'):
self.host = host
self.port = port
self.username = username
self.pwd = pwd
self.__k = None
def run(self):
self.connect()
pass
self.close()
def connect(self):
transport = paramiko.Transport((self.host,self.port))
transport.connect(username=self.username,password=self.pwd)
self.__transport = transport
def close(self):
self.__transport.close()
def cmd(self, command):
ssh = paramiko.SSHClient()
ssh._transport = self.__transport
# 执行命令
stdin, stdout, stderr = ssh.exec_command(command)
# 获取命令结果
result = stdout.read()
return result
def upload(self,local_path, target_path):
# 连接,上传
sftp = paramiko.SFTPClient.from_transport(self.__transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put(local_path, target_path)
ssh = SSHConnection()
ssh.connect()
r1 = ssh.cmd('df')
ssh.upload('s2.py', "/home/alex/s7.py")
ssh.close()
Demo
堡垒机
1、实现思路
程序一:
1、后台管理
- 堡垒机上创建用户和密码(堡垒机root封装的类,UserProfile表)
- .bashrc
/usr/bin/python3 /data/bastion.py
exit
2、后台管理
- 服务器上创建用户和密码 或 公钥上传
- 服务器账号 -> 人 关联
程序二:
3、用户登录
- ssh 堡垒机用户名@堡垒机IP
- 获取当前用户 os.environ['USER']
- 获取当前用户的主机列表
- 获取选中的主机下的所有用户
- 选择任何一个用户
2、表结构
#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liufeng
from sqlalchemy import create_engine, and_, or_, func, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, DateTime
from sqlalchemy.orm import sessionmaker, relationship
from ..conf.settings import engine
Base = declarative_base() # 生成一个SqlORM 基类
#主机表
class Host(Base):
__tablename__ = 'host'
id = Column(Integer, primary_key=True)
hostname = Column(String(64), unique=True, nullable=False)
ip_addr = Column(String(128), unique=True, nullable=False)
port = Column(Integer, default=22)
# 主机用户表
'''
问题:
1、username是否应该设为unique,也有可能是一个堡垒机用户,在两台服务器上用一个登录名,比如root账户
2、name='_host_username_uc是干嘛的
'''
class HostUser(Base):
__tablename__ = 'host_user'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(64), unique=True, nullable=False)
AuthTypes = [
('p', 'SSH/Password'),
('r', 'SSH/KEY'),
]
auth_type = Column(String(16))
cert = Column(String(255))
host_id = Column(Integer, ForeignKey('host.id'))
#外键关联
host = relationship("Host", backref="host_user")
__table_args__ = (
UniqueConstraint('host_id', 'username', name='_host_username_uc'),
)
#用户组
'''
class Group(Base):
__tablename__ = 'group'
id = Column(Integer, primary_key=True)
name = Column(String(64), unique=True, nullable=False)
'''
#堡垒机用户表
class UserProfile(Base):
__tablename__ = 'user_profile'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(64), unique=True, nullable=False)
password = Column(String(255), nullable=False)
#多对多关联
host_user = relationship("host_user", secondary=user_profile_2_host_user, backref="user_profile")
#用户组与堡垒机关系表
'''
class Group2UserProfile(Base):
__tablename__ = 'group_2_user_profile'
id = Column(Integer, primary_key=True, autoincrement=True)
user_profile_id = Column(Integer, ForeignKey('user_profile.id'))
group_id = Column(Integer, ForeignKey('group.id'))
__table_args__ = (
UniqueConstraint('user_profile_id', 'group_id', name='ux_user_group'),
)
'''
#用户组与主机表
'''
class Group2HostUser(Base):
__tablename__ = 'group_2_host_user'
id = Column(Integer, primary_key=True, autoincrement=True)
host_user_id = Column(Integer, ForeignKey('host_user.id'))
group_id = Column(Integer, ForeignKey('group.id'))
__table_args__ = (
UniqueConstraint('group_id', 'host_user_id', name='ux_group_host_user'),
)
'''
#堡垒机用户和主机用户关联表
'''
class UserProfile2HostUser(Base):
__tablename__ = 'user_profile_2_host_user'
id = Column(Integer, primary_key=True, autoincrement=True)
host_user_id = Column(Integer, ForeignKey('host_user.id'))
user_profile_id = Column(Integer, ForeignKey('user_profile.id'))
__table_args__ = (
UniqueConstraint('user_profile_id', 'host_user_id', name='ux_user_host_user'),
)
'''
user_profile_2_host_user = Table("user_profile_2_host_user",Base.metadata,
Column(Integer, ForeignKey('host_user.id')),
Column(Integer, ForeignKey('user_profile.id')),
__table_args__ = (
UniqueConstraint('user_profile_id', 'host_user_id', name='ux_user_host_user')
)
)
#日志表
class AuditLog(Base):
__tablename__ = 'audit_log'
id = Column(Integer, primary_key=True)
'''
日志记录命令、登录,登出三种状态
'''
action_choices2 = [
(u'cmd', u'CMD'),
(u'login', u'Login'),
(u'logout', u'Logout'),
]
action_type = Column(String(16))
log = Column(String(255))
date = Column(DateTime)
user_profile_id = Column(Integer, ForeignKey('user_profile.id'))
host_user_id = Column(Integer, ForeignKey('host_user.id'))
#添加多外键关联
user_profile_id = relationship("audit_log",foreign_keys=[user_profile_id])
host_user_id = relationship("audit_log", foreign_keys=[host_user_id])
表结构
3、实现过程
import paramiko
import os
import sys
import select
import socket
tran = paramiko.Transport(('192.168.7.100', 22,))
tran.start_client()
'''
#使用密钥认证
default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
key = paramiko.RSAKey.from_private_key_file(default_path)
tran.auth_publickey('root', key)
'''
tran.auth_password('root', 'nihao123!') #通过密码认证
chan = tran.open_session()# 打开一个通道
chan.get_pty()# 获取一个终端
chan.invoke_shell()# 激活器
'''
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
'''
while True:
# 监视用户输入和服务器返回数据
# sys.stdin 处理用户输入
# chan 是之前创建的通道,用于接收服务器返回信息
readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1) #监视chen和终端
#只要发生变化,chan或者stdin或者都变化
if chan in readable: #远端有变化后捕获到
try:
x = chan.recv(1024)
#ssh连接后他发送接收数据也是通过socket来做的
if len(x) == 0:
print '\r\n*** EOF\r\n',
break
sys.stdout.write(x)#把内容输入到终端上
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in readable: #当终端有输入捕获到之后
inp = sys.stdin.readline() #把用户的那一行输入
chan.sendall(inp)#发送命令至远端
chan.close()
tran.close()
一行行读入命令
#上面的例子中在捕获输出的时候我们输入的一行命令(字符串)回车之后,sys.stdin才捕获到,这个是默认的终端是这样的,我们就可以打开一个文件记录用户的所有命令操作
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'luo_t'
import paramiko
import os
import sys
import select
import socket
tran = paramiko.Transport(('192.168.7.100', 22,))
tran.start_client()
'''
#使用密钥认证
default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
key = paramiko.RSAKey.from_private_key_file(default_path)
tran.auth_publickey('root', key)
'''
tran.auth_password('root', 'nihao123!') #通过密码认证
chan = tran.open_session()# 打开一个通道
chan.get_pty()# 获取一个终端
chan.invoke_shell()# 激活器
'''
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
'''
log = open('record','ab') #打开一个文件记录用户的输入
while True:
# 监视用户输入和服务器返回数据
# sys.stdin 处理用户输入
# chan 是之前创建的通道,用于接收服务器返回信息
readable, writeable, error = select.select([chan, sys.stdin, ],[],[],1) #坚挺chen和终端
#只要发生变化,chan或者stdin或者都变化
if chan in readable: #远端有变化后捕获到
try:
x = chan.recv(1024)
#ssh连接后他发送接收数据也是通过socket来做的
if len(x) == 0:
log.close() #关闭文件
print '\r\n************************ EOF ************************\r\n',
break
sys.stdout.write(x)#把内容输入到终端上
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in readable: #当终端有输入捕获到之后
inp = sys.stdin.readline() #把用户的那一行输入
log.write(inp) #记录命令
chan.sendall(inp)#发送命令至远端
chan.close()
tran.close()
open_terminal_write_log
一个字符--> stdin
#还有个例子是我们在终端输入命令的时候,经常忘记命令全部的字符。
#默认换行,对于特殊字符特殊处理,比如Ctrl+c
#改变终端默认由行+回车–>stdin,改为一个字符–> stdin
首先我们要做的就是修改终端模式:把原来的默认换行为“回车”,特殊字符特殊处理,改为输入一个字符就捕获并且
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'luo_t'
import paramiko
import os
import sys
import select
import socket
import termios
import tty
tran = paramiko.Transport(('192.168.7.100', 22,))
tran.start_client()
'''
#使用密钥认证
default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
key = paramiko.RSAKey.from_private_key_file(default_path)
tran.auth_publickey('root', key)
'''
tran.auth_password('root', 'nihao123!') #通过密码认证
chan = tran.open_session()# 打开一个通道
chan.get_pty()# 获取一个终端
chan.invoke_shell()# 激活器
'''
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
'''
# 获取原tty属性
oldtty = termios.tcgetattr(sys.stdin)
try:
# 为tty设置新属性
# 默认当前tty设备属性:
# 输入一行回车,执行
# CTRL+C 进程退出,遇到特殊字符,特殊处理。
# 这是为原始模式,不认识所有特殊符号
# 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
tty.setraw(sys.stdin.fileno()) #把远端更换为LINUX原始模式
chan.settimeout(0.0)
while True:
# 监视 用户输入 和 远程服务器返回数据(socket)
# 阻塞,直到句柄可读
r, w, e = select.select([chan, sys.stdin], [], [], 1)
if chan in r:
try:
x = chan.recv(1024)
if len(x) == 0:
print '\r\n*** EOF\r\n',
break
sys.stdout.write(x)
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = sys.stdin.read(1)
if len(x) == 0:
break
chan.send(x)
finally:
# 重新设置终端属性
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
chan.close()
tran.close()
open_terminal_complemented
记录日志,并且不记录tab输入
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'luo_t'
import paramiko
import os
import sys
import select
import socket
import termios
import tty
tran = paramiko.Transport(('192.168.7.100', 22,))
tran.start_client()
'''
#使用密钥认证
default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
key = paramiko.RSAKey.from_private_key_file(default_path)
tran.auth_publickey('root', key)
'''
tran.auth_password('root', 'nihao123!') #通过密码认证
chan = tran.open_session()# 打开一个通道
chan.get_pty()# 获取一个终端
chan.invoke_shell()# 激活器
'''
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
'''
# 获取原tty属性
oldtty = termios.tcgetattr(sys.stdin)
#打开文件
try:
# 为tty设置新属性
# 默认当前tty设备属性:
# 输入一行回车,执行
# CTRL+C 进程退出,遇到特殊字符,特殊处理。
# 这是为原始模式,不认识所有特殊符号
# 放置特殊字符应用在当前终端,如此设置,将所有的用户输入均发送到远程服务器
tty.setraw(sys.stdin.fileno()) #把远端更换为LINUX原始模式
chan.settimeout(0.0)
user_log = open('terminalnew_log','ab')
while True:
# 监视 用户输入 和 远程服务器返回数据(socket)
# 阻塞,直到句柄可读
r, w, e = select.select([chan, sys.stdin], [], [], 1)
if chan in r:
try:
x = chan.recv(1024)
if len(x) == 0:
user_log.close()
print '\r\n*** EOF\r\n',
break
sys.stdout.write(x)
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = sys.stdin.read(1)
if len(x) == 0:
break
if x == '\t': #判断用户的是否为tab如果为tab将不记录
pass
else:
user_log.write(x)#如果用户输入的命令保存至日志
chan.send(x)
finally:
# 重新设置终端属性
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
chan.close()
tran.close()
open_terminal_complemented_new
4、window打开终端
#!/usr/bin/env python
#-*- coding:utf-8 -*-
__author__ = 'luo_t'
import paramiko
import sys
import threading
tran = paramiko.Transport(('192.168.0.111', 22,))
tran.start_client()
'''
#使用密钥认证
default_path = os.path.join(os.environ['root'], '.ssh', 'id_rsa')
key = paramiko.RSAKey.from_private_key_file(default_path)
tran.auth_publickey('root', key)
'''
tran.auth_password('root', 'nihao123!') #通过密码认证
chan = tran.open_session()# 打开一个通道
chan.get_pty()# 获取一个终端
chan.invoke_shell()# 激活器
'''
# 利用sys.stdin,肆意妄为执行操作
# 用户在终端输入内容,并将内容发送至远程服务器
# 远程服务器执行命令,并将结果返回
# 用户终端显示内容
'''
sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n")
def writeall(sock):
while True:
data = sock.recv(256)
'''
SSH发送数据的也是通过socket进行发送数据的,那么我们就可以使用socket来获取远程机器发送回来的数据。
while循环一直接收数据,sock.recv(256)是阻塞的只有数据过来的时候才会继续走。
'''
if not data:
sys.stdout.write('\r\n*** EOF ***\r\n\r\n')
sys.stdout.flush()
break
sys.stdout.write(data)
sys.stdout.flush()
writer = threading.Thread(target=writeall, args=(chan,)) #创建了一个线程,去执行writeall方法,参数为chan(建立的SSH连接)
writer.start()
try:
while True: #主线程循环
d = sys.stdin.read(1) #一直监听用户的输入,输入一个发送一个
if not d:
break
chan.send(d)
except EOFError:
# user hit ^Z or F6
pass
chan.close()
tran.close()
open_terminal_complemented_windows