Protocol
? 和服務(wù)器一樣,也是通過(guò)該類來(lái)實(shí)現(xiàn)。先看一個(gè)簡(jiǎn)短的例程:
from twisted.internet.protocol import Protocol
from sys import stdout
class Echo(Protocol):
def dataReceived(self, data):
stdout.write(data)
在本程序中,只是簡(jiǎn)單的將獲得的數(shù)據(jù)輸出到標(biāo)準(zhǔn)輸出中來(lái)顯示,還有很多其他的事件沒(méi)有作出任何響應(yīng),下面
有一個(gè)回應(yīng)其他事件的例子:
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()
本協(xié)議連接到服務(wù)器,發(fā)送了一個(gè)問(wèn)候消息,然后關(guān)閉了連接。
connectionMade事件通常被用在建立連接的事件發(fā)生時(shí)觸發(fā)。關(guān)閉連接的時(shí)候會(huì)觸發(fā)connectionLost事件函數(shù)
(Simple, single-use clients)簡(jiǎn)單的單用戶客戶端
? 在許多情況下,protocol僅僅是需要連接服務(wù)器一次,并且代碼僅僅是要獲得一個(gè)protocol連接的實(shí)例。在
這樣的情況下,twisted.internet.protocol.ClientCreator提供了一個(gè)恰當(dāng)?shù)腁PI
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負(fù)責(zé)創(chuàng)建Protocol,并且返回相關(guān)事件的連接狀態(tài)。這樣就允許它去做像連接發(fā)生錯(cuò)誤然后
重新連接的事情。這里有一個(gè)ClientFactory的簡(jiǎn)單例子使用Echo協(xié)議并且打印當(dāng)前的連接狀態(tài)
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連接到服務(wù)器,可以使用下面代碼:
from twisted.internet import reactor
reactor.connectTCP(host, port, EchoClientFactory())
reactor.run()
注意:clientConnectionFailed是在Connection不能被建立的時(shí)候調(diào)用,clientConnectionLost是在連接關(guān)閉的時(shí)候被調(diào)用,兩個(gè)是有區(qū)別的。
Reconnection(重新連接)
? 許多時(shí)候,客戶端連接可能由于網(wǎng)絡(luò)錯(cuò)誤經(jīng)常被斷開(kāi)。一個(gè)重新建立連接的方法是在連接斷開(kāi)的時(shí)候調(diào)用
connector.connect()方法。
from twisted.internet.protocol import ClientFactory
class EchoClientFactory(ClientFactory):
def clientConnectionLost(self, connector, reason):
connector.connect()
?? connector是connection和protocol之間的一個(gè)接口被作為第一個(gè)參數(shù)傳遞給clientConnectionLost,
factory能調(diào)用connector.connect()方法重新進(jìn)行連接
?? 然而,許多程序在連接失敗和連接斷開(kāi)進(jìn)行重新連接的時(shí)候使用ReconnectingClientFactory函數(shù)代替這個(gè)
函數(shù),并且不斷的嘗試重新連接。這里有一個(gè)Echo Protocol使用ReconnectingClientFactory的例子:
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
上面的所有例子都非常簡(jiǎn)單,下面是一個(gè)比較復(fù)雜的例子來(lái)自于doc/examples目錄
# 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服務(wù)器,加入了一個(gè)頻道,并且在文件中記錄了所有的通信信息,這表明了在斷開(kāi)連接進(jìn)行重新連接的連接級(jí)別的邏輯以及持久性數(shù)據(jù)是被存儲(chǔ)在Factory的。
Persistent Data in the Factory
? 由于Protocol在每次連接的時(shí)候重建,客戶端需要以某種方式來(lái)記錄數(shù)據(jù)以保證持久化。就好像日志機(jī)器人一樣他需要知道那個(gè)那個(gè)頻道正在登陸,登陸到什么地方去。
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
當(dāng)protocol被創(chuàng)建之后,factory會(huì)獲得他本身的一個(gè)實(shí)例的引用。然后,就能夠在factory中存在他的屬性。
更多的信息:
? 本文檔講述的Protocol類是IProtocol的子類,IProtocol方便的被應(yīng)用在大量的twisted應(yīng)用程序中。要學(xué)習(xí)完整的 IProtocol接口,請(qǐng)參考API文檔IProtocol.
? 在本文檔一些例子中使用的trasport屬性提供了ITCPTransport接口,要學(xué)習(xí)完整的接口,請(qǐng)參考API文檔ITCPTransport
? 接口類是指定對(duì)象有什么方法和屬性以及他們的表現(xiàn)形式的一種方法。參考 Components: Interfaces and Adapters文檔
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

