2.Twisted学习

2018-12-06 07:35:30来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

编写一个客户端

概论

Twisted是一个设计十分灵活的框架,而且允许编写出非常强大的客户端。编写灵活的客户端的只需要你写几个层就可以了。这个文档涵盖了如何创建一个客户端,不包括UDP UDP is covered in a different document .

首先,Protocol类是你通常用来执行和解析协议的地方。这个类每次都twisted.internet.protocol.Protocol 

许多协议的执行继承这个类或者它的子类。一个protocol类会被实例化每当你链接服务器的时候,而且在离开的时候会断开连接。这就是说,持久性的配置不会保存在Protool里。

持久化的类被保存在Factory里边。它继承自twisted.internet.protocol.Factory (or twisted.internet.protocol.ClientFactory 

默认的工厂累只会实例化Protocol只后设置协议的factory属性来指向这个协议。

这是例子:

这个简单的例子只会进行回显。

再来个例子:

看过我翻译的第一章的读者应该很熟悉这两个例子了,就是连接之后就回复,之后关闭。

好吧,开始进入正题,编写一个客户端

在许多例子里,protocol仅仅需要连接服务器一次就可以了,而且代码只想要获得链接协议的第一个实例对象,在这些例子里边,twisted.internet.endpoints提供了合适的API接口,connectProtocol 用的是protocol的实例,而不是工厂的实例!

不管客户端的类型是什么,建立新的连接的方法只是把它通过 connectProtocol和协议实例。这也就是说非常容易改变你链接的状态,不用修改其他部分。举个例子,为了运行C例子(用SSL),只需要实例化 SSL4ClientEndpoint 代替 TCP4ClientEndpoint,为了使用这个优点,发起新的连接的函数和方法通常应该把endpoint作为参数,之后让调用者创建它,而不是使用‘host’和‘port’之类的参数来构建自己的端点。

你可能会看到用以下的代码创建一个客户端,这比较古老了:

1 from twisted.internet.protocol import ClientCreator
2 
3 ...
4 
5 creator = ClientCreator(reactor, Greeter)
6 d = creator.connectTCP("localhost", 1234)
7 d.addCallback(gotProtocol)
8 reactor.run()

客户端工厂

现在依然有许多底层的API在外边,一些特性(比如自动重新连接)还没有用endpoint实现,所以在某些情况下使用它们可能会比较方便。

如果要使用底层api,需要调用reactor.connect。为了实现这些例子,你需要 ClientFactory。这个工厂是负责创建Protocol而且会接受和连接状态有关的事件。它允许你做一些事情,比如在一个连接错误之后再次连接。

给个例子:

 

把它连接到一个服务器,你先需要写一些code:

服务器启动一下,会显示如下:

客户端reactor的api

connectTCP

IReactorTCP.connectTCP提供了IPV4,IPV6的tcp客户端方法。

参数有:host接受地址或者主机名,如果是主机名的话,reactor会自定解析为IP地址(多余开销)

    port接受端口

Reconnection

我们通常都会遇到连接失败的请跨国。一个重新连接的方法:

第一个作为参数被传递的reactor是连接和协议之间的接口。当连接失败的时候,工厂会调用lost方法,之后调用connect()重新连接。

但是,大多数想要实现这个方法的工厂都应该重写ReconnectingClientFactory,而不是像上边那样,这个被重写的方法会尝试连接如果连接lost挥着faild,而且会自动实现梯度延迟。

给你个例子:

高级例子:

这个客户端到目前为止还十分简单。一个更加复杂的例子来了:

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.


"""
An example IRC log bot - logs a channel's events to a file.

If someone says the bot's name in the channel followed by a ':',
e.g.

    <foo> logbot: hello!

the bot will reply:

    <logbot> foo: I am a log bot

Run this script with two arguments, the channel name the bot should
connect to, and file to log to, e.g.:

    $ python ircLogBot.py test test.log

will log channel #test to the file 'test.log'.

To run the script:

    $ python ircLogBot.py <channel> <file>
"""


from __future__ import print_function

# 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 successfully 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))


    # For fun, override the method that determines how a nickname is changed on
    # collisions. The default method appends an underscore.
    def alterCollidedNick(self, nickname):
        """
        Generate an altered version of a nickname that caused a collision in an
        effort to create an unused related name for subsequent registration.
        """
        return nickname + '^'



class LogBotFactory(protocol.ClientFactory):
    """A factory for LogBots.

    A new protocol instance will be created each time we connect to the server.
    """

    def __init__(self, channel, filename):
        self.channel = channel
        self.filename = filename

    def buildProtocol(self, addr):
        p = LogBot()
        p.factory = self
        return p

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

 

工厂中的持久数据

因为每次protocol连接都会创建一个实例,客户端需要以某种方式来跟踪持久化的数据。在日志的记录下,需要知道他正在记录哪一个通道,,以及在哪里对他进行日志记录。

 1 from twisted.words.protocols import irc
 2 from twisted.internet import protocol
 3 
 4 class LogBot(irc.IRCClient):
 5 
 6     def connectionMade(self):
 7         irc.IRCClient.connectionMade(self)
 8         self.logger = MessageLogger(open(self.factory.filename, "a"))
 9         self.logger.log("[connected at %s]" %
10                         time.asctime(time.localtime(time.time())))
11 
12     def signedOn(self):
13         self.join(self.factory.channel)
14 
15 
16 class LogBotFactory(protocol.ClientFactory):
17 
18     def __init__(self, channel, filename):
19         self.channel = channel
20         self.filename = filename

创建协议的时候,它会获得一个工厂的引用,self.factory,它可以在之后访问工厂的属性,比如Logbot,

它打开文件并且连接到工厂中的channel

Factories有一个默认的方法BuildProtocol,它使用protoccol实例来创建。

1 class LogBotFactory(protocol.ClientFactory):
2     protocol = LogBot
3 
4     def __init__(self, channel, filename):
5         self.channel = channel
6         self.filename = filename

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:python装饰器详解

下一篇:撩课-Python-每天5道面试题-第5天