基於Twisted的網絡服務器編寫

開始

此文檔解釋瞭如何使用twisted來實現網絡協議棧的解析和TCP服務的處理。(相同的代碼能夠在SSL和Unix socket servers中複用。)html

protocol處理類通常從twisted.internet.protocol.Protocol中繼承爲子類,大多數的protocol handlers要麼從這個類繼承,或者從相應的子類中再繼承。Protocol類的一個實例是每次鏈接後的實例化,根據實際狀況,當鏈接結束以後被釋放。這意味着這種持久性的配置並不是永存於Protocol中。python

這種永久性的配置其實保存於Factory類中,通常它從 twisted.internet.protocol.Factory 中繼承。這個工廠類的buildProtocol()方法在每次鏈接到來時用於構建一個Protocol對象。react

在一般狀況下,對多端口或者網絡地址上來提供相同的服務是很是有用的。這就是爲什麼工廠Factory並不提供監聽鏈接的緣由,實際上它並不知道任何關於網絡方面的事情。api

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()app

上述的代碼中,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,這是一個將工廠鏈接到網絡的方法。

如下是簡單的聊天會話記錄:

相關文章
相關標籤/搜索