文档章节

基于Twisted的网络服务器编写

刺猬一号
 刺猬一号
发布于 2017/06/21 00:16
字数 2024
阅读 89
收藏 1

开始

此文档解释了如何使用twisted来实现网络协议栈的解析和TCP服务的处理。(相同的代码可以在SSL和Unix socket servers中复用。)

protocol处理类一般从twisted.internet.protocol.Protocol中继承为子类,大多数的protocol handlers要么从这个类继承,或者从相应的子类中再继承。Protocol类的一个实例是每次连接后的实例化,根据实际情况,当连接结束之后被释放。这意味着这种持久性的配置并非永存于Protocol中。

这种永久性的配置其实保存于Factory类中,一般它从 twisted.internet.protocol.Factory 中继承。这个工厂类的buildProtocol()方法在每次连接到来时用于构建一个Protocol对象。

在通常情况下,对多端口或者网络地址上来提供相同的服务是非常有用的。这就是为何工厂Factory并不提供监听连接的原因,实际上它并不知道任何关于网络方面的事情。

Protocols

上面提到,它在大多数代码中存在的形式是伴随着一种附加类和函数。Twisted的protocol主要以异步的方式处理数据:当网络中的事件到达时,协议才响应事件。事件到达后,会调用协议中的方法。以下是个简单的例子:

from twisted.internet.protocol import Protocol  
class Echo(Protocol):  
    def dataReceived(self, data):  
        self.transport.write(data)  

这是一个非常简单的协议。它只是简单的回写所有接收到的数据,并不响应任何事件。以下是另外一个响应事件的协议:

from twisted.internet.protocol import Protocol  
class QOTD(Protocol):  
def connectionMade(self):  
        self.transport.write("An apple a day keeps the doctor away\r\n")   
        self.transport.loseConnection() 

 

这个协议有一个双引号来响应建立的初始连接,然后结束这个连接。connectionMade是一个事件,通常创建于对象的连接发生后,以及所有的初始greetings(如上QOTD协议,它主要基于RFC865).connectionLost事件是对已经处理的所有连接请求指定对象的销毁。以下是例子:

from twisted.internet.protocol import Protocol  
class Echo(Protocol):  
    def __init__(self, factory):  
        self.factory = factory  
    def connectionMade(self):  
        self.factory.numProtocols = self.factory.numProtocols+1   
        self.transport.write(  
            "Welcome! There are currently %d open connections.\n" %  
            (self.factory.numProtocols,))  
    def connectionLost(self, reason):  
        self.factory.numProtocols = self.factory.numProtocols-1  
    def dataReceived(self, data):  
        self.transport.write(data)  

这里的connectionMade和connectionLost事件相互合作在一个共享对象factory中存放一个活动的协议对象总数。当创建一个实例时Factory必须传递给Echo.__init__。Factory用于共享当前的一些状态,这些状态将超出任何给定连接的生命周期。在下一部分内容中你将看到为何把这个对象称为”factory”

loseConnection()和abortConnection()

上述的代码中,loseConnection()在写入传输通道后被调用。loseConnection()方法在所有的数据被Twisted写入操作系统后,它将会关闭连接。所以在这种情况下不用担心通道写入的的数据会被丢失,它是很安全的

如果”生产者”被用于这种通信传输上时,一旦“生产者”被注销,loseConnection()将仅仅关闭连接,传输的数据有可能未成功写入。

在多数情况下,等待所有的数据被成功写出并非我们所想象。由于网络的失效,bug或者其它连接的恶意攻击,即使loseConnection被调用,连接还建立的情况下,这时写入传输通道的数据有时可能依旧无法传递。在这种情况下,abortConnection可以被很好的使用。它不管当前未传输的缓存中是否有数据,或者在“生产者”依旧处于注册的状态下,它都将立刻关闭连接。注意:abortConnection仅仅在高于或等于Twisted11.1版本上才有效。

Using the Protocol

在这部分,你将学会怎样去运行一个使用你自己的协议的服务器。以前面讨论过QOTD服务器,下面将运行这个服务器:

from twisted.internet.protocol import Factory  
from twisted.internet.endpoints import TCP4ServerEndpoint  
from twisted.internet import reactor  
class QOTDFactory(Factory):  
    def buildProtocol(self, addr):  
        return QOTD()  
# 8007 is the port you want to run under. Choose something >1024  
endpoint = TCP4ServerEndpoint(reactor, 8007)  
endpoint.listen(QOTDFactory())  
reactor.run()  

在这个例子中,它创建了一个协议工厂QOTDFactory,它的主要任务是创建一个QOTD协议的实例,所以通过buildProtocol()方法来返回一个QOTD类的实例。然后,开始监听TCP端口,所以让TCP4ServerEndpoint来识别要绑定的端口地址,最后给它的listen方法传递一个协议工厂对象即可。

由于这是个简短的代码,不需要其它任何东西来启动Twisted reactor. Endpoint.listen告诉reactor通过使用一个特殊的协议(由协议工厂实例化的)来处理连接到endpoint的地址的所有连接请求,但是reactor在它要做事之前必须先run一下,所以reactor.run()用于启动reactor然后一直等待你所期望的到达此端口的任何连接请求。

通过Control-C或者调用reactor.stop()来停止reactor.

 

Helper Protocols

许多协议创建于类似的抽象的底层协议。最流行的互联网协议是基于行的。“行”经常用CR-LF来终止。然而,更多的其它协议是混合的,他们有基于行的部分和原始数据部分。这样的例子包括Http/1.1和Freenet协议。

在大多数情况下,会有LineReceiver协议,这个协议将区分两个不同的事件处理: lineReceived和rawDataReceived.缺省情况下每行数据到达行,lineReceived会被调用。然而,如果setRawMode被调用后,协议将采用rawDataReceived方法来接收数据,除非再调用setLineMode来切换到缺省情况。它也提供sendLine方法,它在传传的数据后面会自动的增加”\r\n”

以下是个使用行接收的例子:

from twisted.protocols.basic import LineReceiver  
class Answer(LineReceiver):  
    answers = {'How are you?': 'Fine', None : "I don't know what you mean"}  
    def lineReceived(self, line):  
        if self.answers.has_key(line):  
            self.sendLine(self.answers[line])  
        else:  
            self.sendLine(self.answers[None])  

注意:在这种情况下就不要再增加\r\n了。

Factories

简单协议创建

对于一个工厂来说,它的主要工作是实例化某个指定协议类的实化。有一个更简单的方法来实现一个工厂。缺省的实现是通过buildProtocol方法调用工厂的protocol属性来创建一个协议实例。这种方式可以让每种协议进行各种的访问,做各种修改,来完成这种配置。以下是代码:

from twisted.internet.protocol import Factory, Protocol  
from twisted.internet.endpoints import TCP4ServerEndpoint  
from twisted.internet import reactor  
class QOTD(Protocol):  
    def connectionMade(self):  
        # self.factory was set by the factory's default buildProtocol:  
        self.transport.write(self.factory.quote + '\r\n')  
        self.transport.loseConnection()  
class QOTDFactory(Factory):  
    # This will be used by the default buildProtocol to create new protocols:  
    protocol = QOTD  
    def __init__(self, quote=None):  
        self.quote = quote or 'An apple a day keeps the doctor away'  
endpoint = TCP4ServerEndpoint(reactor, 8007)  
endpoint.listen(QOTDFactory("configurable quote"))  
reactor.run()  

工厂的启动与关闭

工厂具有两种方式来执行相关应用的创建与销毁。以下是例子:

from twisted.internet.protocol import Factory  
from twisted.protocols.basic import LineReceiver  
class LoggingProtocol(LineReceiver):  
    def lineReceived(self, line):  
        self.factory.fp.write(line+'\n')  
class LogfileFactory(Factory):  
    protocol = LoggingProtocol  
    def __init__(self, fileName):  
        self.file = fileName  
    def startFactory(self):  
        self.fp = open(self.file, 'a')  
    def stopFactory(self):  
        self.fp.close()  

综合

以下是最后一个例子,有一个最简单的聊天服务器允许多用户选择用户名然后与其它用户进行通信。它演示了在工厂中如何使用共享的状态,共享每个独立协议的状态机,以及在不同协议之间的通信情况。

from twisted.internet.protocol import Factory  
from twisted.protocols.basic import LineReceiver  
from twisted.internet import reactor  
  
class Chat(LineReceiver):  
    def __init__(self, users):  
        self.users = users  
        self.name = None  
        self.state = "GETNAME"  
  
    def connectionMade(self):  
        self.sendLine("What's your name?")  
  
    def connectionLost(self, reason):  
        if self.users.has_key(self.name):  
            del self.users[self.name]  
  
    def lineReceived(self, line):  
        if self.state == "GETNAME":  
            self.handle_GETNAME(line)  
        else:  
            self.handle_CHAT(line)  
  
    def handle_GETNAME(self, name):  
        if self.users.has_key(name):  
            self.sendLine("Name taken, please choose another.")  
            return  
        self.sendLine("Welcome, %s!" % (name,))  
        self.name = name  
        self.users[name] = self  
        self.state = "CHAT"  
  
    def handle_CHAT(self, message):  
        message = "<%s> %s" % (self.name, message)  
        for name, protocol in self.users.iteritems():  
            if protocol != self:  
                protocol.sendLine(message)  
  
class ChatFactory(Factory):  
  
    def __init__(self):  
        self.users = {} # maps user names to Chat instances  
  
    def buildProtocol(self, addr):  
        return Chat(self.users)  
  
reactor.listenTCP(8123, ChatFactory())  
reactor.run()  

唯一不熟的API有可能就是listenTCP,这是一个将工厂连接到网络的方法。

以下是简单的聊天会话记录:

© 著作权归作者所有

共有 人打赏支持
刺猬一号
粉丝 12
博文 372
码字总数 615097
作品 0
深圳
私信 提问
【转】Python Twisted介绍

Python Twisted介绍 作者:Jessica McKellar 原文链接   Twisted是用Python实现的基于事件驱动的网络引擎框架。Twisted诞生于2000年初,在当时的网络游戏开发者看来,无论他们使用哪种语言...

罗兵
2016/06/27
0
0
zg手册 之 twisted 开发(1)-- twisted 框架介绍

异步非阻塞框架 twisted 是一个事件驱动的网络开发框架,使用 python 开发。 twisted 框架编写的服务器有几个基本的元素: 应用程序对象(application):管理应用程序资源的对象,一个应用程序...

东昕
2014/06/21
0
0
Python网络编程笔记

一、说明 使用套接字进行网络编程,需要先了解一些有关网络编程的背景信息。 1、客户端/服务器架构: 服务器为一个或多个客户端提供所需的服务,存在的目的就是等待客户端的请求,并响应它们,...

PeanutLike
2016/12/01
62
0
Firefly —— 国产的 Python 游戏服务器框架

Firefly是免费、开源、稳定、快速扩展、能 “热更新”的分布式游戏服务器端框架,采用Python编写,基于Twisted框架开发。它包括了开发框架和数据库缓存服务等各种游戏服务器基础服务,节省大...

oschina
2013/08/19
35
0
jamiesun/PandaRSS

#PandaRSS PandaRSS 是一个基于 ToughRADIUS V2版本 API 的自助服务系统。 快速指南 运行环境 Linux Python 2.7 pip Twisted>=15.0.0 可选 bottle>=0.12.7 安装 pip install -U https://git......

jamiesun
2016/04/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

探索802.11ax

802.11ax承诺在真实条件下改善峰值性能和最差情况。 如何改善今天的Wi-Fi? 在决定如何改进当前版本以外的Wi-Fi时,802.11ac,IEEE和Wi-Fi联盟调查了Wi-Fi部署和行为,以确定更广泛使用的障碍...

linuxprobe16
43分钟前
2
0
使用linux将64G的SDCARD格式化为FAT32

一、命令如下: sudo fdisk -lsudo mkfs.vfat /dev/sda -Isudo fdisk /dev/sda Welcome to fdisk (util-linux 2.29.2). Changes will remain in memory only, until you decide to wri......

mbzhong
今天
4
0
深入理解Plasma(四):Plasma Cash

这一系列文章将围绕以太坊的二层扩容框架,介绍其基本运行原理,具体操作细节,安全性讨论以及未来研究方向等。本篇文章主要介绍在 Plasma 框架下的项目 Plasma Cash。 深入理解Plasma(1):...

HiBlock
昨天
1
0
命令参数的三大风格:Posix、BSD、GNU

今天读到命令行中参数的风格有三大类,即Unix/Posix、BSD、GNU。分别有以下特征: Unix/Posix风格,即命令后的参数,可以分组,便必须以连字符开头,如ps -aux。 BSD风格,即命令后的参数,可...

大别阿郎
昨天
2
0
PHP生成图片验证码

PHP生成图片验证码 /** * PHP生成图片验证码 * Class VerifyImage */class VerifyImage{ // 生成随机字串 private $verifyCode; // 图片对象 private $image; /**...

DrChenXX
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部