Protocol
和服務器一樣,也是通過該類來實現。先看一個簡短的例程:
1
2
3
4
5
6
|
from twisted.internet.protocol import Protocol from sys import stdout class Echo(Protocol): def dataReceived( self , data): stdout.write(data) |
在本程序中,只是簡單的將獲得的數據輸出到標準輸出中來顯示,還有很多其他的事件沒有作出任何響應,下面
有一個回應其他事件的例子:
1
2
3
4
5
6
|
from twisted.internet.protocol import Protocol class WelcomeMessage(Protocol): def connectionMade( self ): self .transport.write( "Hello server, I am the client!/r/n" ) self .transport.loseConnection() |
本協議連接到服務器,發送了一個問候消息,然后關閉了連接。
connectionMade事件通常被用在建立連接的事件發生時觸發。關閉連接的時候會觸發connectionLost事件函數
(Simple, single-use clients)簡單的單用戶客戶端
在許多情況下,protocol僅僅是需要連接服務器一次,并且代碼僅僅是要獲得一個protocol連接的實例。在
這樣的情況下,twisted.internet.protocol.ClientCreator提供了一個恰當的API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from twisted.internet import reactor from twisted.internet.protocol import Protocol, ClientCreator class Greeter(Protocol): def sendMessage( self , msg): self .transport.write( "MESSAGE %s/n" % msg) def gotProtocol(p): p.sendMessage( "Hello" ) reactor.callLater( 1 , p.sendMessage, "This is sent in a second" ) reactor.callLater( 2 , p.transport.loseConnection) c = ClientCreator(reactor, Greeter) c.connectTCP( "localhost" , 1234 ).addCallback(gotProtocol) |
ClientFactory(客戶工廠)
ClientFactory負責創建Protocol,并且返回相關事件的連接狀態。這樣就允許它去做像連接發生錯誤然后
重新連接的事情。這里有一個ClientFactory的簡單例子使用Echo協議并且打印當前的連接狀態
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from twisted.internet.protocol import Protocol, ClientFactory from sys import stdout class Echo(Protocol): def dataReceived( self , data): stdout.write(data) class EchoClientFactory(ClientFactory): def startedConnecting( self , connector): print 'Started to connect.' def buildProtocol( self , addr): print 'Connected.' return Echo() def clientConnectionLost( self , connector, reason): print 'Lost connection. Reason:' , reason def clientConnectionFailed( self , connector, reason): print 'Connection failed. Reason:' , reason |
要想將EchoClientFactory連接到服務器,可以使用下面代碼:
1
2
3
|
from twisted.internet import reactor reactor.connectTCP(host, port, EchoClientFactory()) reactor.run() |
注意:clientConnectionFailed是在Connection不能被建立的時候調用,clientConnectionLost是在連接關閉的時候被調用,兩個是有區別的。
Reconnection(重新連接)
許多時候,客戶端連接可能由于網絡錯誤經常被斷開。一個重新建立連接的方法是在連接斷開的時候調用
connector.connect()方法。
1
2
3
4
5
|
from twisted.internet.protocol import ClientFactory class EchoClientFactory(ClientFactory): def clientConnectionLost( self , connector, reason): connector.connect() |
connector是connection和protocol之間的一個接口被作為第一個參數傳遞給clientConnectionLost,
factory能調用connector.connect()方法重新進行連接
然而,許多程序在連接失敗和連接斷開進行重新連接的時候使用ReconnectingClientFactory函數代替這個
函數,并且不斷的嘗試重新連接。這里有一個Echo Protocol使用ReconnectingClientFactory的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
from twisted.internet.protocol import Protocol, ReconnectingClientFactory from sys import stdout class Echo(Protocol): def dataReceived( self , data): stdout.write(data) class EchoClientFactory(ReconnectingClientFactory): def startedConnecting( self , connector): print 'Started to connect.' def buildProtocol( self , addr): print 'Connected.' print 'Resetting reconnection delay' self .resetDelay() return Echo() def clientConnectionLost( self , connector, reason): print 'Lost connection. Reason:' , reason ReconnectingClientFactory.clientConnectionLost( self , connector, reason) def clientConnectionFailed( self , connector, reason): print 'Connection failed. Reason:' , reason ReconnectingClientFactory.clientConnectionFailed( self , connector,reason) |
A Higher-Level Example: ircLogBot
上面的所有例子都非常簡單,下面是一個比較復雜的例子來自于doc/examples目錄
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
# twisted imports from twisted.words.protocols import irc from twisted.internet import reactor, protocol from twisted.python import log # system imports import time, sys class MessageLogger: """ An independent logger class (because separation of application and protocol logic is a good thing). """ def __init__( self , file ): self . file = file def log( self , message): """Write a message to the file.""" timestamp = time.strftime( "[%H:%M:%S]" , time.localtime(time.time())) self . file .write( '%s %s/n' % (timestamp, message)) self . file .flush() def close( self ): self . file .close() class LogBot(irc.IRCClient): """A logging IRC bot.""" nickname = "twistedbot" def connectionMade( self ): irc.IRCClient.connectionMade( self ) self .logger = MessageLogger( open ( self .factory.filename, "a" )) self .logger.log( "[connected at %s]" % time.asctime(time.localtime(time.time()))) def connectionLost( self , reason): irc.IRCClient.connectionLost( self , reason) self .logger.log( "[disconnected at %s]" % time.asctime(time.localtime(time.time()))) self .logger.close() # callbacks for events def signedOn( self ): """Called when bot has succesfully signed on to server.""" self .join( self .factory.channel) def joined( self , channel): """This will get called when the bot joins the channel.""" self .logger.log( "[I have joined %s]" % channel) def privmsg( self , user, channel, msg): """This will get called when the bot receives a message.""" user = user.split( '!' , 1 )[ 0 ] self .logger.log( "<%s> %s" % (user, msg)) # Check to see if they're sending me a private message if channel = = self .nickname: msg = "It isn't nice to whisper! Play nice with the group." self .msg(user, msg) return # Otherwise check to see if it is a message directed at me if msg.startswith( self .nickname + ":" ): msg = "%s: I am a log bot" % user self .msg(channel, msg) self .logger.log( "<%s> %s" % ( self .nickname, msg)) def action( self , user, channel, msg): """This will get called when the bot sees someone do an action.""" user = user.split( '!' , 1 )[ 0 ] self .logger.log( "* %s %s" % (user, msg)) # irc callbacks def irc_NICK( self , prefix, params): """Called when an IRC user changes their nickname.""" old_nick = prefix.split( '!' )[ 0 ] new_nick = params[ 0 ] self .logger.log( "%s is now known as %s" % (old_nick, new_nick)) class LogBotFactory(protocol.ClientFactory): """A factory for LogBots. A new protocol instance will be created each time we connect to the server. """ # the class of the protocol to build when new connection is made protocol = LogBot def __init__( self , channel, filename): self .channel = channel self .filename = filename def clientConnectionLost( self , connector, reason): """If we get disconnected, reconnect to server.""" connector.connect() def clientConnectionFailed( self , connector, reason): print "connection failed:" , reason reactor.stop() if __name__ = = '__main__' : # initialize logging log.startLogging(sys.stdout) # create factory protocol and application f = LogBotFactory(sys.argv[ 1 ], sys.argv[ 2 ]) # connect factory to this host and port reactor.connectTCP( "irc.freenode.net" , 6667 , f) # run bot reactor.run() |
ircLogBot.py 連接到了IRC服務器,加入了一個頻道,并且在文件中記錄了所有的通信信息,這表明了在斷開連接進行重新連接的連接級別的邏輯以及持久性數據是被存儲在Factory的。
Persistent Data in the Factory
由于Protocol在每次連接的時候重建,客戶端需要以某種方式來記錄數據以保證持久化。就好像日志機器人一樣他需要知道那個那個頻道正在登陸,登陸到什么地方去。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
from twisted.internet import protocol from twisted.protocols import irc class LogBot(irc.IRCClient): def connectionMade( self ): irc.IRCClient.connectionMade( self ) self .logger = MessageLogger( open ( self .factory.filename, "a" )) self .logger.log( "[connected at %s]" % time.asctime(time.localtime(time.time()))) def signedOn( self ): self .join( self .factory.channel) class LogBotFactory(protocol.ClientFactory): protocol = LogBot def __init__( self , channel, filename): self .channel = channel self .filename = filename |
當protocol被創建之后,factory會獲得他本身的一個實例的引用。然后,就能夠在factory中存在他的屬性。
更多的信息:
本文檔講述的Protocol類是IProtocol的子類,IProtocol方便的被應用在大量的twisted應用程序中。要學習完整的 IProtocol接口,請參考API文檔IProtocol.
在本文檔一些例子中使用的trasport屬性提供了ITCPTransport接口,要學習完整的接口,請參考API文檔ITCPTransport
接口類是指定對象有什么方法和屬性以及他們的表現形式的一種方法。參考 Components: Interfaces and Adapters文檔