python——客户端

发布时间:2019-07-15 10:45:51编辑:auto阅读(1257)

    客户端


    综述


    twisted是一个设计非常灵活的框架,通过它可以写出功能强大的客户端,然而要在代码中使用非常多的层次结构。这个文档包括创建用于TCP,SSL和Unix sockets的客户端

    在 底层,实际上完成协议语法和处理的是Protocol类。这个类通常是来自于twisted.internet.protocol.Protocol。大 多数的protocol handlers继承自这个类或它的子类。protocol类的一个实例将在你连接到服务器时被初始化,在断开连接时结束。这意味着持久的配置不会被保存 在Protocol中。

    持久的配置将会保存在Factory类中,它通常继承自 twisted.internet.protocol.Factory(或者 twisted.internet.protocol.ClientFactory)。默认的factory类仅仅实例化Protocol,并且设置 factory属性指向自己。这使得Protocol可以访问、修改和持久配置。

    Protocol 


    这个类将会是代码中使用最多的类,是twisted异步处理数据的一个协议,这意味着这个协议从不等待一个事件,它只是响应从网络中到来的事件。


    下面是一个简单的例子:   

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

    这是一个简单的例子,将从连接进来的程序读到的数据显示到标准输出上。下面的例子中Protocol会相应另外一个事件:


    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通常在Protocol对象出现时创建,connectionLost在Protocol断开时创建。


    简单的,单用户客户端


    大多数情况,protocol仅需要连接服务器一次,并且代码只是想获得一个protocol的连接实例。正是由于这些原因,twisted.internet.endpoints提供了一些合适的API,特别是其中的connectProtocol提供了一个protocol实例而不是factory。

        from twisted.internet import reactor
        from twisted.internet.protocol import Protocol
        from twisted.internet.endpoints import TCP4ClientEndpoint,connectProtocol
        
        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)
            
        point = TCP4ClientEndpoint(reactor, "localhost", 1234)
        d = connectProtocol(point, Greeter())
        d.addCallback(gotProtocol)
        reactor.run()

        

    ClientFactory


    from twisted.internet.protocol import Protocol, ClientFactory
    from sys import stdout
    
    class Echo(Protocol):
        def dataReceived(self, connector):
            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 clientConnectionFaild(self, connector, reason):
            print 'Connection failded. Reason', reason

    要连接这个EchoClientFactory到一个服务器上,可以使用下面的代码:

    from twisted.internet import reactor
    reactor.connectTCP(host, port, EchoClientFactory())
    reactor.run()

    注意:函数connectConnectionFailed在连接不

    能established别调用,而clientConnectionLost在连接完成并且断开时被调用。


    Reactor Client APIs


    connectTCP


    IReactorTCP.connectTCP 提供对IPV4和IPV6客户端的支持,它接收的host参数可以是主机名,也可以是IP地址,在连接之前reactor会自动的将主机名解析为IP地址。这也意味着如果一个主机名有多个IP地址解析时,重新连接时不一定总是能连接到相同的主机。这说明在每次连接之前都会进行域名解析。如果创建了很多短连接(每秒几百次或几千次),它会首先将主机名解析为IP地址,然后将地址传给connectTCP。


    Reconnection


    通常,当一个客户端连接因为网络问题中断的时候,在断开之后一个重新连接的方法叫做connector.connect()

    from twisted.internet.protocol import ClientFactory
    
    class EchoClientFactory(ClientFactory):
        def clientConnectionLost(self, connector, reason):
            connector.connect()

    当连接出错,同时factory收到一个clientConnectionLost的事件通知时,factory通过调用connector.connect()来重新启动一个连接。然而,大多数程序想要实现重连这个功能应该实现ReconnectingClientFactory,下面是一个通过ReconnectingClientFactory实现的Echo protocol:

    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 'Reseting reconnection delay'
            self.resetDelay()
            return Echo()
            
        def clientConnectionLost(self, connector, reason):
            print 'Lost connection. Reason:', reason
            ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
            
        def clientConnectionFailded(self, connector, reason):
            print 'Connection failded. Reason:', reason
            ReconnectingClientFactory.clientConnectionFailed(self, connector, reason)


    高级例子:ircLogBot


    上面客户端的例子都很简单,下面是一个更加复杂的例子

    ircLogBot.py

    # 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 botRun this script with two arguments, the channel name the bot shouldconnect to, and file to log to, e.g.:    $ python ircLogBot.py test test.logwill log channel #test to the file 'test.log'.To run the script:    $ python ircLogBot.py <channel> <file>"""# twisted importsfrom twisted.words.protocols import ircfrom twisted.internet import reactor, protocolfrom twisted.python import log# system importsimport time, sysclass 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))
    
    
        # 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()


    ircLogBot.py 连接到一个IRC服务器,连接一个通道,所有的日志和通信通过它传送到一个文件。


    Persistent Data in the Factory


    由于Protocol实例在每次连接建立的时候都会重新创建,客户端需要对一些需要持久连接的数据进行追踪。

    from twisted.woeds.protocols import irc
    from twisted.internet import protocol
    
    class LogBot(irc.IRCClient):
        def connectionMade(self):
            irc.IRCClient.connectionMade(self)
            self.logger = MessageLogger(open(self.factory.filename, "a"))
            self.log.log("[connected at %s]" % time.asctime(time.localtime(time.time())))
            
        def signedOn(self):
             self.join(self.factory.channel)
             
    class LogBotFactory(protocol.ClientFactory):
        
        def __init__(self, channel, filename):
            self.channel = channel
            self.filename = filename
        def buildProtocol(self, addr):
            p = LogBot()
            p.factory = self
            return p

    当protocol创建时,会得到一个factory的引用self.factory,它能访问factory的属性。在logBot的例子中,它打开文件并连接到存储在factory中的通道。




关键字