在之前博客C/S架构的网络编程中,IO多路复用是将多个IO操作复用到1个服务端进程中进行处理,即无论有多少个客户端进行连接请求,服务端始终只有1个进程对客户端进行响应,这样的好处是节省了系统开销(select不适合单个客户端长会话操作,这样其它客户端连接请求就会一直等待,poll/epoll对select进行了改进)。下面介绍结合了IO多路复用和多进程(多线程)的SocketServer模块。
1 SocketServer模块
SocketServer内部使用IO多路复用以及“多线程”和“多进程” ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会创建一个“线程”或者“进程”专门负责处理当前客户端的所有请求。
SocketServer与select/poll/epoll的本质区别:客户端第1次连接时,服务端就为该客户端创建一个线程或进程,此后服务端就利用此线程或进程与客户端进行通信,后续的数据传输几乎不要server端的直接参与。如果服务端创建的是进程,那么client1和client2同时向server端传输数据时是互不影响的;如果服务端创建的是线程(python中多线程,在同一时间只有一个线程在运行,底层会自动进行上下文切换,即python中不存在真正的多线程),那么client1和client2交替上传数据。 知识回顾: python中的多线程,有一个GIL(全局解释器锁)限制在同一时刻只有1个线程在运行,底层自动进行上下文切换,保证多个线程轮流运行(cpu切片),也就是python中不存在真正的多线程问题,伪多线程,实际多个线程不能真正实现并发处理。 |
中间处理过程如图所示
1.1 ThreadingTCPServer
ThreadingTCPServer实现的Soket服务器内部会为每个client创建一个 “线程”,该线程用来和客户端进行交互。
1、ThreadingTCPServer基础
使用ThreadingTCPServer要求:
(1)创建一个继承自SocketServer.BaseRequestHandler的类
(2)类中必须定义一个名称为 handle 的方法
(3)启动ThreadingTCPServer
#!/usr/bin/env python
# -*- coding:utf-8 -*- import SocketServer class MyServer(SocketServer.BaseRequestHandler): def handle(self):
# print self.request,self.client_address,self.server
conn = self.request
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
Flag = True
while Flag:
data = conn.recv(1024)
if data == 'exit':
Flag = False
elif data == '':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.') if __name__ == '__main__':
server = SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
server.serve_forever()
SocketServer实现的服务端
#!/usr/bin/env python
# -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5) while True:
data = sk.recv(1024)
print 'receive:',data
inp = raw_input('please input:')
sk.sendall(inp)
if inp == 'exit':
break sk.close()
客户端
2、ThreadingTCPServer源码剖析
ThreadingTCPServer的类图关系如下:
注:实际上在类的继承时,子类会继承父类的方法,所以我们在分析类的继承关系时,直接把父类的方法放到子类中,这样就直观些,在python中还有1点要注意的是子类到底是继承哪个父类的方法,因为python中存在多继承。
内部调用流程为
启动服务端程序 1、执行TCPServer.__init__ 方法,创建服务端Socket对象并绑定IP和端口 2、执行BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler的类MyRequestHandle赋值给self.RequestHandlerClass 3、执行BaseServer.server_forever方法,While 循环一直监听是否有客户端请求到达 ... 当客户端连接到达服务器 |
ThreadingTCPServer相关源码
class BaseServer: """Base class for server classes. Methods for the caller: - __init__(server_address, RequestHandlerClass)
- serve_forever(poll_interval=0.5)
- shutdown()
- handle_request() # if you do not use serve_forever()
- fileno() -> int # for select() Methods that may be overridden: - server_bind()
- server_activate()
- get_request() -> request, client_address
- handle_timeout()
- verify_request(request, client_address)
- server_close()
- process_request(request, client_address)
- shutdown_request(request)
- close_request(request)
- handle_error() Methods for derived classes: - finish_request(request, client_address) Class variables that may be overridden by derived classes or
instances: - timeout
- address_family
- socket_type
- allow_reuse_address Instance variables: - RequestHandlerClass
- socket """ timeout = None def __init__(self, server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass
self.__is_shut_down = threading.Event()
self.__shutdown_request = False def server_activate(self):
"""Called by constructor to activate the server. May be overridden. """
pass def serve_forever(self, poll_interval=0.5):
"""Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores
self.timeout. If you need to do periodic tasks, do them in
another thread.
"""
self.__is_shut_down.clear()
try:
while not self.__shutdown_request:
# XXX: Consider using another file descriptor or
# connecting to the socket to wake this up instead of
# polling. Polling reduces our responsiveness to a
# shutdown request and wastes cpu at all other times.
r, w, e = _eintr_retry(select.select, [self], [], [],
poll_interval)
if self in r:
self._handle_request_noblock()
finally:
self.__shutdown_request = False
self.__is_shut_down.set() def shutdown(self):
"""Stops the serve_forever loop. Blocks until the loop has finished. This must be called while
serve_forever() is running in another thread, or it will
deadlock.
"""
self.__shutdown_request = True
self.__is_shut_down.wait() # The distinction between handling, getting, processing and
# finishing a request is fairly arbitrary. Remember:
#
# - handle_request() is the top-level call. It calls
# select, get_request(), verify_request() and process_request()
# - get_request() is different for stream or datagram sockets
# - process_request() is the place that may fork a new process
# or create a new thread to finish the request
# - finish_request() instantiates the request handler class;
# this constructor will handle the request all by itself def handle_request(self):
"""Handle one request, possibly blocking. Respects self.timeout.
"""
# Support people who used socket.settimeout() to escape
# handle_request before self.timeout was available.
timeout = self.socket.gettimeout()
if timeout is None:
timeout = self.timeout
elif self.timeout is not None:
timeout = min(timeout, self.timeout)
fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
if not fd_sets[0]:
self.handle_timeout()
return
self._handle_request_noblock() def _handle_request_noblock(self):
"""Handle one request, without blocking. I assume that select.select has returned that the socket is
readable before this function was called, so there should be
no risk of blocking in get_request().
"""
try:
request, client_address = self.get_request()
except socket.error:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.shutdown_request(request) def handle_timeout(self):
"""Called if no new request arrives within self.timeout. Overridden by ForkingMixIn.
"""
pass def verify_request(self, request, client_address):
"""Verify the request. May be overridden. Return True if we should proceed with this request. """
return True def process_request(self, request, client_address):
"""Call finish_request. Overridden by ForkingMixIn and ThreadingMixIn. """
self.finish_request(request, client_address)
self.shutdown_request(request) def server_close(self):
"""Called to clean-up the server. May be overridden. """
pass def finish_request(self, request, client_address):
"""Finish one request by instantiating RequestHandlerClass."""
self.RequestHandlerClass(request, client_address, self) def shutdown_request(self, request):
"""Called to shutdown and close an individual request."""
self.close_request(request) def close_request(self, request):
"""Called to clean up an individual request."""
pass def handle_error(self, request, client_address):
"""Handle an error gracefully. May be overridden. The default is to print a traceback and continue. """
print '-'*40
print 'Exception happened during processing of request from',
print client_address
import traceback
traceback.print_exc() # XXX But this goes to stderr!
print '-'*40
BaseServer
class TCPServer(BaseServer): """Base class for various socket-based server classes. Defaults to synchronous IP stream (i.e., TCP). Methods for the caller: - __init__(server_address, RequestHandlerClass, bind_and_activate=True)
- serve_forever(poll_interval=0.5)
- shutdown()
- handle_request() # if you don't use serve_forever()
- fileno() -> int # for select() Methods that may be overridden: - server_bind()
- server_activate()
- get_request() -> request, client_address
- handle_timeout()
- verify_request(request, client_address)
- process_request(request, client_address)
- shutdown_request(request)
- close_request(request)
- handle_error() Methods for derived classes: - finish_request(request, client_address) Class variables that may be overridden by derived classes or
instances: - timeout
- address_family
- socket_type
- request_queue_size (only for stream sockets)
- allow_reuse_address Instance variables: - server_address
- RequestHandlerClass
- socket """ address_family = socket.AF_INET socket_type = socket.SOCK_STREAM request_queue_size = 5 allow_reuse_address = False def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise def server_bind(self):
"""Called by constructor to bind the socket. May be overridden. """
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname() def server_activate(self):
"""Called by constructor to activate the server. May be overridden. """
self.socket.listen(self.request_queue_size) def server_close(self):
"""Called to clean-up the server. May be overridden. """
self.socket.close() def fileno(self):
"""Return socket file number. Interface required by select(). """
return self.socket.fileno() def get_request(self):
"""Get the request and client address from the socket. May be overridden. """
return self.socket.accept() def shutdown_request(self, request):
"""Called to shutdown and close an individual request."""
try:
#explicitly shutdown. socket.close() merely releases
#the socket and waits for GC to perform the actual close.
request.shutdown(socket.SHUT_WR)
except socket.error:
pass #some platforms may raise ENOTCONN here
self.close_request(request) def close_request(self, request):
"""Called to clean up an individual request."""
request.close()
TCPServer
class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the
# main process
daemon_threads = False def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread. In addition, exception handling is done here. """
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request) def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start() 复制代码
ThreadingMixIn
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
ThreadingTCPServer
RequestHandler相关源码
class BaseRequestHandler: """Base class for request handler classes. This class is instantiated for each request to be handled. The
constructor sets the instance variables request, client_address
and server, and then calls the handle() method. To implement a
specific service, all you need to do is to derive a class which
defines a handle() method. The handle() method can find the request as self.request, the
client address as self.client_address, and the server (in case it
needs access to per-server information) as self.server. Since a
separate instance is created for each request, the handle() method
can define arbitrary other instance variariables. """ def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish() def setup(self):
pass def handle(self):
pass def finish(self):
pass
SocketServer.BaseRequestHandler
源码精简
import socket
import threading
import select def process(request, client_address):
print request,client_address
conn = request
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
flag = True
while flag:
data = conn.recv(1024)
if data == 'exit':
flag = False
elif data == '':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.') sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(('127.0.0.1',8002))
sk.listen(5) while True:
r, w, e = select.select([sk,],[],[],1)
print 'looping'
if sk in r:
print 'get request'
request, client_address = sk.accept()
t = threading.Thread(target=process, args=(request, client_address))
t.daemon = False
t.start() sk.close()
模拟SocketServer代码
从精简代码可以看出,SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于select和Threading两个东西,其实本质上就是在服务器端为每一个客户端创建一个线程,用于后续的数据处理,当前线程用来处理对应客户端的请求,所以可以支持同时n个客户端链接(长连接)。
1.2 ForkingTCPServer
ForkingTCPServer与ThreadingTCPServer的使用和执行流程基本一致,只不过在内部分别为请求者建立“进程”和“线程”。
基本使用:
#!/usr/bin/env python
# -*- coding:utf-8 -*- import SocketServer class MyServer(SocketServer.BaseRequestHandler): def handle(self):
# print self.request,self.client_address,self.server
conn = self.request
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
Flag = True
while Flag:
data = conn.recv(1024)
if data == 'exit':
Flag = False
elif data == '':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.') if __name__ == '__main__':
server = SocketServer.ForkingTCPServer(('127.0.0.1',8009),MyServer)
server.serve_forever()
服务端
#!/usr/bin/env python
# -*- coding:utf-8 -*- import socket ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5) while True:
data = sk.recv(1024)
print 'receive:',data
inp = raw_input('please input:')
sk.sendall(inp)
if inp == 'exit':
break sk.close()
客户端
以上ForkingTCPServer只是将 ThreadingTCPServer 实例中的代码:
server = SocketServer.ThreadingTCPServer(( '127.0.0.1' , 8009 ),MyRequestHandler)
变更为: server = SocketServer.ForkingTCPServer(( '127.0.0.1' , 8009 ),MyRequestHandler)
|
SocketServer的ThreadingTCPServer之所以可以同时处理请求得益于select和os.fork两个东西,其实本质上就是在服务器端为每一个客户端创建一个进程,用于后续数据处理,当前新创建的进程用来处理对应客户端的请求,所以,可以支持同时n个客户端链接(长连接)。
源码剖析参考 ThreadingTCPServer
2 Twisted模块
使用传统的BIO(Blocking IO/阻塞IO)进行网络编程时,进行网络IO读写时都会阻塞当前线程,如果实现一个TCP服务器,都需要对每个客户端连接开启一个线程,而很多线程可能都在傻傻的阻塞住等待读写数据,系统资源消耗大。
Twisted是用Python实现的基于事件驱动的网络引擎框架,功能非常丰富,基本包括了常用的网络组件,例如:网络协议、线程、数据库管理、网络操作、电子邮件等。
编程框架,即别人预先定义好的一个框架(一个项目),如.net某个web框架有25个class,从BeginRequest依次执行类里的process方法。程序员自定义一个类添加到框架里,应用程序则从上到下运行,就会执行自定义代码。框架只知道这个类的列表,不关心具体内容,从上到下执行,类似于一个执行链,C#里叫委托链。也就是把代码类放到这个列表中,委托这个框架替你执行。
事件驱动(not event),把自定义代码注册到框架中,框架代替你执行。或者框架提供几个接口,让你插入数据(python无接口概念,只有事件驱动)。委托不能为空,事件可以为空。通俗来讲,所谓事件驱动,就是说程序就像是一个报警器(reactor),时刻等待着外部事件(event),诸如有人入侵等,一旦有事件发生,程序就会触发一些特定的操作(callback),注入拨打报警电话等。
简而言之,事件驱动分为二个部分:第一,注册事件;第二,触发事件。
自定义事件驱动框架如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*- #event_drive.py event_list = [] def run():
for event in event_list:
obj = event()
obj.execute() class BaseHandler(object):
"""
用户必须继承该类,从而规范所有类的方法(类似于接口的功能)
"""
def execute(self):
raise Exception('you must overwrite execute')
自定义事件驱动框架代码
程序员使用该自定义事件驱动框架
#!/usr/bin/env python
# -*- coding:utf-8 -*- from source import event_drive class MyHandler(event_drive.BaseHandler): def execute(self):
print 'event-drive execute MyHandler' event_drive.event_list.append(MyHandler)
event_drive.run()
程序员使用该框架代码
执行过程:
1 导入自定义框架event_drive 2 自定义类MyClass,这个类必须继承event_drive中的BaseHandler类 3 MyClass类中重载execute方法 4 注册事件到框架的委托链,把自定义的类MyClass加入到事件列表event_list中(下面的Twisted框架是创建对象后改一个字段为类名也是同样的目的) 5 执行run方法 事件驱动只不过是框架规定了执行顺序,程序员在使用框架时,可以向原执行顺序中注册“事件”,从而在框架执行时可以出发已注册 的“事件”。 |
基于事件驱动Twisted模块的Socket
(1)由于twisted是第三方模块,默认没有安装,需要先安装
cd Twisted-15.5.0 python setup.py build #编译 python setup.py install #安装 上述安装方法适用于windows和linux的命令行安装,实际上也可以直接执行python setup.py install 注意:twisted依赖于zope和win32api模块,需要先安装依赖。 |
(2)实例
#!/usr/bin/env python
# -*- coding:utf-8 -*- from twisted.internet import protocol
from twisted.internet import reactor class Echo(protocol.Protocol): #继承protocol.py中的Protocol类
def dataReceived(self, data):
self.transport.write(data) #将收到的内容直接发送回去 def main():
factory = protocol.ServerFactory() #实例化
factory.protocol = Echo #将自定义类传给对象 reactor.listenTCP(8000,factory) #将端口和实例化对象作为参数传给reactor
reactor.run() if __name__ == '__main__':
main()
服务端
import socket ip_port=('127.0.0.1',8000)
sk=socket.socket()
sk.connect(ip_port)
sk.settimeout(5) while True:
inp=raw_input("please input:")
sk.sendall(inp)
print sk.recv(1024) sk.close()
客户端
源码类图关系
实例执行流程
运行服务端程序 创建Protocol的派生类Echo 创建ServerFactory对象,并将Echo类封装到其protocol字段中 执行reactor的listenTCP方法,内部使用tcp.Port创建socket server对象,并将该对象添加到了reactor的set类型的字段_read 中 执行reactor的run方法,内部执行while循环,并通过select来监视_read中文件描述符是否有变化,循环中... 客户端请求到达 |
从源码可以看出,上述实例本质上使用了事件驱动的方法 和 IO多路复用的机制来进行Socket的处理。
Pycharm debug模式调试调用关系
Twisted优点:
1、使用基于事件驱动的编程模型,而不是多线程模型。
2、跨平台:为主流操作系统平台暴露出的事件通知系统提供统一的接口。
3、“内置电池”的能力:提供流行的应用层协议实现,因此Twisted马上就可为开发人员所用。
4、符合RFC规范,已经通过健壮的测试套件证明了其一致性。
5、能很容易的配合多个网络协议一起使用。
6、可扩展。
更多Twisted内容,请参考:
http://www.cnblogs.com/c9com/archive/2013/01/05/2845552.html(Twisted reactor解剖)
http://www.cnblogs.com/zhangjing0502/archive/2012/07/11/2586666.html(Twisted的网络通信模型)
http://www.cnblogs.com/zhangjing0502/archive/2012/07/11/2586575.html(Python中reactor,factory,protocol)
http://www.cnblogs.com/zhangjing0502/archive/2012/05/16/2504415.html(Twisted异步编程--Deferred)
http://www.cnblogs.com/zhangjing0502/archive/2012/05/30/2526552.html([Python-Twisted] Twisted入门之端口转发服务器)
http://www.cnblogs.com/Rex7/p/4752581.html(跟踪 twisted 里deferred 的Callback)
3 paramiko模块
linux运维中都需要对服务器进行配置,如果服务器数量较多,那么可以进行远程自动化批量操作。在python中的paramiko模块就是实现远程执行命令的模块。使用paramiko模块仅需要在本地安装相应的模块(pycrypto以及paramiko模块),对远程服务器没有配置要求,paramiko模块基于ssh协议,实现对远程服务器的相关操作,对于连接多台服务器,进行复杂的连接操作特别有帮助。
3.1 paramiko安装
1 windows下的安装paramiko (1)解压pycrypto-2.6.tar.gz源码到路径C:\Python27\Lib\site-packages (2)在windows控制台下进入目录pycrypto-2.6,依次执行python setup.py build和python setup.py install window是如果没有安装编译器,那么会报错,解决办法是安装VCForPython27.msi(Microsoft Visual C++ Compiler for Python 2.7) 编译过程中会出现“Unable to find vcvarsall.bat”的错误,解决方法参考http://blog.csdn.net/donger_soft/article/details/44838109 测试是否安装成功:在python命令行下输入:import pycrypto,检查是否报错 (3)解压paramiko-1.10.1.tar.gz源码到路径C:\Python27\Lib\site-packages (4)在windows控制台下进入目录paramiko-1.10.1,依次执行python setup.py build和python setup.py install 测试是否安装成功:在python命令行下输入:import paramiko,检查是否报错 2 ubuntu下的安装paramiko |
3.2 paramiko使用
SSHClient方法
#!/usr/bin/env python
#-*- coding:utf-8 -*- import paramiko ssh = paramiko.SSHClient() #创建ssh对象
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #允许连接不在know_hosts文件中的主机
ssh.connect(hostname='192.168.1.100',port=22,username='root',password='') #hostname='主机名'或'ip地址'
stdin,stdout,stderror = ssh.exec_command('df') #在远程服务器执行命令df print stdout.read() #获取命令结果
print stderror.read() #如果命令执行错误,则返回标准错误输出
ssh.close() #关闭连接
实例1:在远程服务器执行命令
#!/usr/bin/env python
#-*- coding:utf-8 -*- import paramiko transport = paramiko.Transport(('192.168.7.100',22)) #创建transport对象
transport.connect(username='root',password='nihao123!')#调用连接方法connect ssh = paramiko.SSHClient() #创建ssh对象
ssh._transport = transport #把上面创建的transport对象赋值给ssh对象中的_transport字段 stdin,stdout,stderr = ssh.exec_command('ifconfig') #执行命令ifconfig print stdout.read()
print stderr.read() transport.close()
实例2:在远程服务器执行命令
在上述两个实例中,其实实例1中connect内部封装了Transport,即:
ssh = paramiko.SSHClient()
t = self._transport = Transport(sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds)
注意:在操作文件时只能用实例2的方法
SFTPClient方法
#!/usr/bin/env python
#-*- coding:utf-8 -*- import paramiko private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa') ssh = paramiko.SSHClient() #创建ssh对象
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #允许连接不在know_host文件中的的主机
ssh.connect(hostname='192.168.1.100',port=22,username='root',pkey=private_key) #连接服务器
stdin,stdout,stderr = ssh.exec_command('ifconfig') #执行命令
print stdout.read() #获取命令执行结果
ssh.close() '''
如果是运维人员这里不需要看
1、首先创建一个公钥和私钥
ssh-keygen
2、复制id_rsa.pub至要被远程执行命令的机器,并把id_rsa.pub里的内容增加至authorized_keys文件中
如果authorized_keys文件不存在创建即可
'''
实例5:基于用户名密码的上传下载
#!/usr/bin/env python
#-*- coding:utf-8 -*- import paramiko private_key = paramiko.RSAKey.from_private_key_file('/root/.ssh/id_rsa') transport = paramiko.Transport(('192.168.1.100',22))
transport.connect(username='root',pkey=private_key) #连接 sftp = paramiko.SFTPClient.from_transport(transport)#创建SFTPClient对象 sftp.put('test.zip','/tmp/test.zip') #将test.zip上传到目标机器的/tmp/目录下,并命名为test.zip
sftp.get('/tmp/messages.log','test.log') #下载目标服务器/tmp/messages.log 到本地,并命名为test.log transport.close()
实例6:基于用户名密钥对的上传下载
在远程服务器执行命令时,其实时间主要消耗在建立连接上了。
自定义类的,在连接后进行相应的上传下载操作,这样就可以在一次连接中进行其它操作,避免频繁的创建连接,关闭连接,减少资源消耗
#!/usr/bin/env python
#-*- coding:utf-8 -*- import paramiko
import uuid class DownUpLoad(object):
def __init__(self,ip,port,user,passwd):
self.hostname = ip
self.port = port
self.username = user
self.password = passwd def create_file(self):
file_name = str(uuid.uuid4()) #uuid.uuid4()会生成一个文件UUID,当作文件名
with open(file_name,'wb') as f:
f.write('This is test file!')
return file_name def run(self):
self.connect()
self.upload()
self.rename()
self.close() def connect(self): #连接方法
transport = paramiko.Transport((self.hostname, self.port)) #创建一个连接对象
transport.connect(username=self.username, password=self.password)#调用transport对象中的连接方法
self.__transport = transport #把transport赋值给__transport def close(self): #关闭连接
self.__transport.close() def upload(self): #上传文件方法
file_name = self.create_file() #创建文件
sftp = paramiko.SFTPClient.from_transport(self.__transport) #创建基于transport连接的SFTPClient
sftp.put(file_name,'/tmp/test.txt') #上传文件 def rename(self): #执行命名方法
ssh = paramiko.SSHClient() #建立ssh对象
ssh._transport = self.__transport #替换ssh_transport字段为self.__transport
stdin,stdout,stderr = ssh.exec_command('mv /tmp/test1 /tmp/test2') #执行命令
print stdout.read() #读取执行命令 if __name__ == '__main__':
ha = DownUpLoad()
ha.run()
自定义包含连接和上传下载方法的类
参考资料:
http://www.cnblogs.com/wupeiqi/articles/5095821.html
http://www.cnblogs.com/wupeiqi/articles/5040823.html
http://www.cnblogs.com/luotianshuai/p/5111587.html
http://www.cnblogs.com/luotianshuai/p/5131053.html