Python下的twisted框架入门指引_python

什么是twisted?

twisted是一个用python语言写的事件驱动的网络框架,他支持很多种协议,包括UDP,TCP,TLS和其他应用层协议,比如HTTP,SMTP,NNTM,IRC,XMPP/Jabber。 非常好的一点是twisted实现和很多应用层的协议,开发人员可以直接只用这些协议的实现。其实要修改Twisted的SSH服务器端实现非常简单。很多时候,开发人员需要实现protocol类。

一个Twisted程序由reactor发起的主循环和一些回调函数组成。当事件发生了,比如一个client连接到了server,这时候服务器端的事件会被触发执行。
用Twisted写一个简单的TCP服务器

下面的代码是一个TCPServer,这个server记录客户端发来的数据信息。

==== code1.py ====
import sys
from twisted.internet.protocol import ServerFactory
from twisted.protocols.basic import LineReceiver
from twisted.python import log
from twisted.internet import reactor

class CmdProtocol(LineReceiver):

  delimiter = '\n'

  def connectionMade(self):
    self.client_ip = self.transport.getPeer()[1]
    log.msg("Client connection from %s" % self.client_ip)
    if len(self.factory.clients) >= self.factory.clients_max:
      log.msg("Too many connections. bye !")
      self.client_ip = None
      self.transport.loseConnection()
    else:
      self.factory.clients.append(self.client_ip)

  def connectionLost(self, reason):
    log.msg('Lost client connection. Reason: %s' % reason)
    if self.client_ip:
      self.factory.clients.remove(self.client_ip)

  def lineReceived(self, line):
    log.msg('Cmd received from %s : %s' % (self.client_ip, line))

class MyFactory(ServerFactory):

  protocol = CmdProtocol

  def __init__(self, clients_max=10):
    self.clients_max = clients_max
    self.clients = []

log.startLogging(sys.stdout)
reactor.listenTCP(9999, MyFactory(2))
reactor.run()

下面的代码至关重要:

from twisted.internet import reactor
reactor.run()

这两行代码会启动reator的主循环。

在上面的代码中我们创建了"ServerFactory"类,这个工厂类负责返回“CmdProtocol”的实例。 每一个连接都由实例化的“CmdProtocol”实例来做处理。 Twisted的reactor会在TCP连接上后自动创建CmdProtocol的实例。如你所见,protocol类的方法都对应着一种事件处理。

当client连上server之后会触发“connectionMade"方法,在这个方法中你可以做一些鉴权之类的操作,也可以限制客户端的连接总数。每一个protocol的实例都有一个工厂的引用,使用self.factory可以访问所在的工厂实例。

上面实现的”CmdProtocol“是twisted.protocols.basic.LineReceiver的子类,LineReceiver类会将客户端发送的数据按照换行符分隔,每到一个换行符都会触发lineReceived方法。稍后我们可以增强LineReceived来解析命令。

Twisted实现了自己的日志系统,这里我们配置将日志输出到stdout

当执行reactor.listenTCP时我们将工厂绑定到了9999端口开始监听。

user@lab:~/TMP$ python code1.py
2011-08-29 13:32:32+0200 [-] Log opened.
2011-08-29 13:32:32+0200 [-] __main__.MyFactory starting on 9999
2011-08-29 13:32:32+0200 [-] Starting factory <__main__.MyFactory instance at 0x227e320
2011-08-29 13:32:35+0200 [__main__.MyFactory] Client connection from 127.0.0.1
2011-08-29 13:32:38+0200 [CmdProtocol,0,127.0.0.1] Cmd received from 127.0.0.1 : hello server

使用Twisted来调用外部进程

下面我们给前面的server添加一个命令,通过这个命令可以读取/var/log/syslog的内容

import sys
import os

from twisted.internet.protocol import ServerFactory, ProcessProtocol
from twisted.protocols.basic import LineReceiver
from twisted.python import log
from twisted.internet import reactor

class TailProtocol(ProcessProtocol):
  def __init__(self, write_callback):
    self.write = write_callback

  def outReceived(self, data):
    self.write("Begin lastlog\n")
    data = [line for line in data.split('\n') if not line.startswith('==')]
    for d in data:
      self.write(d + '\n')
    self.write("End lastlog\n")

  def processEnded(self, reason):
    if reason.value.exitCode != 0:
      log.msg(reason)

class CmdProtocol(LineReceiver):

  delimiter = '\n'

  def processCmd(self, line):
    if line.startswith('lastlog'):
      tailProtocol = TailProtocol(self.transport.write)
      reactor.spawnProcess(tailProtocol, '/usr/bin/tail', args=['/usr/bin/tail', '-10', '/var/log/syslog'])
    elif line.startswith('exit'):
      self.transport.loseConnection()
    else:
      self.transport.write('Command not found.\n')

  def connectionMade(self):
    self.client_ip = self.transport.getPeer()[1]
    log.msg("Client connection from %s" % self.client_ip)
    if len(self.factory.clients) >= self.factory.clients_max:
      log.msg("Too many connections. bye !")
      self.client_ip = None
      self.transport.loseConnection()
    else:
      self.factory.clients.append(self.client_ip)

  def connectionLost(self, reason):
    log.msg('Lost client connection. Reason: %s' % reason)
    if self.client_ip:
      self.factory.clients.remove(self.client_ip)

  def lineReceived(self, line):
    log.msg('Cmd received from %s : %s' % (self.client_ip, line))
    self.processCmd(line)

class MyFactory(ServerFactory):

  protocol = CmdProtocol

  def __init__(self, clients_max=10):
    self.clients_max = clients_max
    self.clients = []

log.startLogging(sys.stdout)
reactor.listenTCP(9999, MyFactory(2))
reactor.run()

在上面的代码中,没从客户端接收到一行内容后会执行processCmd方法,如果收到的一行内容是exit命令,那么服务器端会断开连接,如果收到的是lastlog,我们要吐出一个子进程来执行tail命令,并将tail命令的输出重定向到客户端。这里我们需要实现ProcessProtocol类,需要重写该类的processEnded方法和outReceived方法。在tail命令有输出时会执行outReceived方法,当进程退出时会执行processEnded方法。

如下是执行结果样例:

user@lab:~/TMP$ python code2.py
2011-08-29 15:13:38+0200 [-] Log opened.
2011-08-29 15:13:38+0200 [-] __main__.MyFactory starting on 9999
2011-08-29 15:13:38+0200 [-] Starting factory <__main__.MyFactory instance at 0x1a5a3f8>
2011-08-29 15:13:47+0200 [__main__.MyFactory] Client connection from 127.0.0.1
2011-08-29 15:13:58+0200 [CmdProtocol,0,127.0.0.1] Cmd received from 127.0.0.1 : test
2011-08-29 15:14:02+0200 [CmdProtocol,0,127.0.0.1] Cmd received from 127.0.0.1 : lastlog
2011-08-29 15:14:05+0200 [CmdProtocol,0,127.0.0.1] Cmd received from 127.0.0.1 : exit
2011-08-29 15:14:05+0200 [CmdProtocol,0,127.0.0.1] Lost client connection. Reason: [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionDone'>: Connection was closed cleanly.

可以使用下面的命令作为客户端发起命令:

user@lab:~$ netcat 127.0.0.1 9999
test
Command not found.
lastlog
Begin lastlog
Aug 29 15:02:03 lab sSMTP[5919]: Unable to locate mail
Aug 29 15:02:03 lab sSMTP[5919]: Cannot open mail:25
Aug 29 15:02:03 lab CRON[4945]: (CRON) error (grandchild #4947 failed with exit status 1)
Aug 29 15:02:03 lab sSMTP[5922]: Unable to locate mail
Aug 29 15:02:03 lab sSMTP[5922]: Cannot open mail:25
Aug 29 15:02:03 lab CRON[4945]: (logcheck) MAIL (mailed 1 byte of output; but got status 0x0001, #012)
Aug 29 15:05:01 lab CRON[5925]: (root) CMD (command -v debian-sa1 > /dev/null && debian-sa1 1 1)
Aug 29 15:10:01 lab CRON[5930]: (root) CMD (test -x /usr/lib/atsar/atsa1 && /usr/lib/atsar/atsa1)
Aug 29 15:10:01 lab CRON[5928]: (CRON) error (grandchild #5930 failed with exit status 1)
Aug 29 15:13:21 lab pulseaudio[3361]: ratelimit.c: 387 events suppressed

End lastlog
exit

使用Deferred对象

reactor是一个循环,这个循环在等待事件的发生。 这里的事件可以是数据库操作,也可以是长时间的计算操作。 只要这些操作可以返回一个Deferred对象。Deferred对象可以自动得在事件发生时触发回调函数。reactor会block当前代码的执行。

现在我们要使用Defferred对象来计算SHA1哈希。

import sys
import os
import hashlib

from twisted.internet.protocol import ServerFactory, ProcessProtocol
from twisted.protocols.basic import LineReceiver
from twisted.python import log
from twisted.internet import reactor, threads

class TailProtocol(ProcessProtocol):
  def __init__(self, write_callback):
    self.write = write_callback

  def outReceived(self, data):
    self.write("Begin lastlog\n")
    data = [line for line in data.split('\n') if not line.startswith('==')]
    for d in data:
      self.write(d + '\n')
    self.write("End lastlog\n")

  def processEnded(self, reason):
    if reason.value.exitCode != 0:
      log.msg(reason)

class HashCompute(object):
  def __init__(self, path, write_callback):
    self.path = path
    self.write = write_callback

  def blockingMethod(self):
    os.path.isfile(self.path)
    data = file(self.path).read()
    # uncomment to add more delay
    # import time
    # time.sleep(10)
    return hashlib.sha1(data).hexdigest()

  def compute(self):
    d = threads.deferToThread(self.blockingMethod)
    d.addCallback(self.ret)
    d.addErrback(self.err)

  def ret(self, hdata):
    self.write("File hash is : %s\n" % hdata)

  def err(self, failure):
    self.write("An error occured : %s\n" % failure.getErrorMessage())

class CmdProtocol(LineReceiver):

  delimiter = '\n'

  def processCmd(self, line):
    if line.startswith('lastlog'):
      tailProtocol = TailProtocol(self.transport.write)
      reactor.spawnProcess(tailProtocol, '/usr/bin/tail', args=['/usr/bin/tail', '-10', '/var/log/syslog'])
    elif line.startswith('comphash'):
      try:
        useless, path = line.split(' ')
      except:
        self.transport.write('Please provide a path.\n')
        return
      hc = HashCompute(path, self.transport.write)
      hc.compute()
    elif line.startswith('exit'):
      self.transport.loseConnection()
    else:
      self.transport.write('Command not found.\n')

  def connectionMade(self):
    self.client_ip = self.transport.getPeer()[1]
    log.msg("Client connection from %s" % self.client_ip)
    if len(self.factory.clients) >= self.factory.clients_max:
      log.msg("Too many connections. bye !")
      self.client_ip = None
      self.transport.loseConnection()
    else:
      self.factory.clients.append(self.client_ip)

  def connectionLost(self, reason):
    log.msg('Lost client connection. Reason: %s' % reason)
    if self.client_ip:
      self.factory.clients.remove(self.client_ip)

  def lineReceived(self, line):
    log.msg('Cmd received from %s : %s' % (self.client_ip, line))
    self.processCmd(line)

class MyFactory(ServerFactory):

  protocol = CmdProtocol

  def __init__(self, clients_max=10):
    self.clients_max = clients_max
    self.clients = []

log.startLogging(sys.stdout)
reactor.listenTCP(9999, MyFactory(2))
reactor.run()

blockingMethod从文件系统读取一个文件计算SHA1,这里我们使用twisted的deferToThread方法,这个方法返回一个Deferred对象。这里的Deferred对象是调用后马上就返回了,这样主进程就可以继续执行处理其他的事件。当传给deferToThread的方法执行完毕后会马上触发其回调函数。如果执行中出错,blockingMethod方法会抛出异常。如果成功执行会通过hdata的ret返回计算的结果。
推荐的twisted阅读资料

http://twistedmatrix.com/documents/current/core/howto/defer.html http://twistedmatrix.com/documents/current/core/howto/process.html http://twistedmatrix.com/documents/current/core/howto/servers.html

API文档:

http://twistedmatrix.com/documents/current/api/twisted.html

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索python
python框架 twisted、twisted 入门、twisted框架、twisted软件框架、twisted框架介绍,以便于您获取更多的相关知识。

时间: 2024-09-17 03:44:12

Python下的twisted框架入门指引_python的相关文章

Python的Django框架使用入门指引_python

 前言 传统 Web 开发方式常常需要编写繁琐乏味的重复性代码,不仅页面表现与逻辑实现的代码混杂在一起,而且代码编写效率不高.对于开发者来说,选择一个功能强大并且操作简洁的开发框架来辅助完成繁杂的编码工作,将会对开发效率的提升起到很大帮助.幸运的是,这样的开发框架并不少见,需要做的仅是从中选出恰恰为开发者量身打造的那款Web框架. 自从基于 MVC 分层结构的 Web 设计理念普及以来,选择适合的开发框架无疑是项目成功的关键性因素.无论是 Struts.Spring 或是其他 Web 框架的出现

极简的Python入门指引_python

初试牛刀 假设你希望学习Python这门语言,却苦于找不到一个简短而全面的入门教程.那么本教程将花费十分钟的时间带你走入Python的大门.本文的内容介于教程(Toturial)和速查手册(CheatSheet)之间,因此只会包含一些基本概念.很显然,如果你希望真正学好一门语言,你还是需要亲自动手实践的.在此,我会假定你已经有了一定的编程基础,因此我会跳过大部分非Python语言的相关内容.本文将高亮显示重要的关键字,以便你可以很容易看到它们.另外需要注意的是,由于本教程篇幅有限,有很多内容我会

使用C语言扩展Python程序的简单入门指引_python

一.简介 Python是一门功能强大的高级脚本语言,它的强大不仅表现在其自身的功能上,而且还表现在其良好的可扩展性上,正因如此,Python已经开始受到越来越多人的青睐,并且被屡屡成功地应用于各类大型软件系统的开发过程中. 与其它普通脚本语言有所不同,Python程序员可以借助Python语言提供的API,使用C或者C++来对Python进行功能性扩展,从而即可以利用Python方便灵活的语法和功能,又可以获得与C或者C++几乎相同的执行性能.执行速度慢是几乎所有脚本语言都具有的共性,也是倍受人

Python中的Matplotlib模块入门教程_python

1 关于 Matplotlib 模块 Matplotlib 是一个由 John Hunter 等开发的,用以绘制二维图形的 Python 模块.它利用了 Python 下的数值计算模块 Numeric 及 Numarray,克隆了许多 Matlab 中的函数, 用以帮助用户轻松地获得高质量的二维图形.Matplotlib 可以绘制多种形式的图形包括普通的线图,直方图,饼图,散点图以及误差线图等:可以比较方便的定制图形的各种属性比如图线的类型,颜色,粗细,字体的大小等:它能够很好地支持一部分 Te

Python的Flask框架中Flask-Admin库的简单入门指引_python

 Flask-Admin是一个功能齐全.简单易用的Flask扩展,让你可以为Flask应用程序增加管理界面.它受django-admin包的影响,但用这样一种方式实现,开发者拥有最终应用程序的外观.感觉和功能的全部控制权. 本文是关于Flask-Admin库的快速入门.本文假设读者预先具有一些Flask框架的知识.     介绍     初始化     增加视图     身份验证     生成URL     模型视图     文件管理 介绍 这个库打算做到尽可能的灵活.并且开发者不需要任何猴子补

简单的编程0基础下Python入门指引_python

 你曾经想知道计算机是如何工作的吗?尽管我们不能在一篇文章里面教会你所有的东西,但是可以通过学习如何写出你自己的程序来获得一个良好的开端.在这篇Python教程中,你将会学到计算机编程的基础知识,使用对新手来说最棒的编程语言之一.什么是编程? 尽可能简单的讲,编程是编写代码,命令计算机去完成某项任务的艺术.这里讲的某项任务,可以是简单的两数相加,或是像把飞船送入轨道这样的复杂任务! 一个程序里面,最小的组成部分被称作语句(statement)--代表了对计算机做出的一条指令. 当你完成了自己的程

Python下的subprocess模块的入门指引_python

在熟悉了Qt的QProcess以后,再回头来看python的subprocess总算不觉得像以前那么恐怖了. 和QProcess一样,subprocess的目标是启动一个新的进程并与之进行通讯.subprocess.Popen 这个模块主要就提供一个类Popen: class subprocess.Popen( args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, clos

Python中的元类编程入门指引_python

回顾面向对象编程 让我们先用 30 秒钟来回顾一下 OOP 到底是什么.在面向对象编程语言中,可以定义 类,它们的用途是将相关的数据和行为捆绑在一起.这些类可以继承其 父类的部分或全部性质,但也可以定义自己的属性(数据)或方法(行为).在定义类的过程结束时,类通常充当用来创建 实例(有时也简单地称为 对象)的模板.同一个类的不同实例通常有不同的数据,但"外表"都是一样 - 例如, Employee 对象 bob 和 jane 都有 .salary 和 .room_number ,但两者

用PyQt进行Python图形界面的程序的开发的入门指引_python

一般来说,选择用于应用程序的 GUI 工具箱会是一件棘手的事.使用 Python(许多语言也一样)的程序员可以选择的 GUI 工具箱种类繁多,而每个工具箱都有各自的优缺点.有些速度比其它工具箱快,有些比较小:有些易于安装,有些更适合于跨平台使用(对于这一点,还要指出,有些支持您需要满足的特定特性).当然,各种库都相应具有各种许可证. 对于 Python 程序员而言,缺省的 GUI 选择是 Tk(通过 Tkinter 绑定)- 其原因显而易见.Tkinter 和闲置的 IDE 是由 Python