使用Python的Twisted框架编写简单的网络客户端


Posted in Python onApril 16, 2015

Protocol
  和服务器一样,也是通过该类来实现。先看一个简短的例程:

from twisted.internet.protocol import Protocol
from sys import stdout

class Echo(Protocol):
  def dataReceived(self, data):
    stdout.write(data)

在本程序中,只是简单的将获得的数据输出到标准输出中来显示,还有很多其他的事件没有作出任何响应,下面
有一个回应其他事件的例子:

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

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协议并且打印当前的连接状态

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连接到服务器,可以使用下面代码:

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

注意:clientConnectionFailed是在Connection不能被建立的时候调用,clientConnectionLost是在连接关闭的时候被调用,两个是有区别的。

Reconnection(重新连接)
  许多时候,客户端连接可能由于网络错误经常被断开。一个重新建立连接的方法是在连接断开的时候调用

connector.connect()方法。

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的例子:

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目录

# 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在每次连接的时候重建,客户端需要以某种方式来记录数据以保证持久化。就好像日志机器人一样他需要知道那个那个频道正在登陆,登陆到什么地方去。

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文档

Python 相关文章推荐
Python下的Mysql模块MySQLdb安装详解
Apr 09 Python
Python 序列化 pickle/cPickle模块使用介绍
Nov 30 Python
利用 Monkey 命令操作屏幕快速滑动
Dec 07 Python
linecache模块加载和缓存文件内容详解
Jan 11 Python
python3+dlib实现人脸识别和情绪分析
Apr 21 Python
Python模拟百度自动输入搜索功能的实例
Feb 14 Python
python三引号输出方法
Feb 27 Python
Python实现代码统计工具
Sep 19 Python
Python hashlib常见摘要算法详解
Jan 13 Python
pandas apply多线程实现代码
Aug 17 Python
python读取excel数据并且画图的实现示例
Feb 08 Python
Python爬虫爬取ts碎片视频+验证码登录功能
Feb 22 Python
从Python的源码浅要剖析Python的内存管理
Apr 16 #Python
用Python实现换行符转换的脚本的教程
Apr 16 #Python
Python下的subprocess模块的入门指引
Apr 16 #Python
Python下的twisted框架入门指引
Apr 15 #Python
Python代码调试的几种方法总结
Apr 15 #Python
详解Python中with语句的用法
Apr 15 #Python
python获取本机外网ip的方法
Apr 15 #Python
You might like
PHP中调用ASP.NET的WebService的代码
2011/04/22 PHP
分享一个PHP数据流应用的简单例子
2012/06/01 PHP
php PDO实现的事务回滚示例
2017/03/23 PHP
php微信公众号开发之关键词回复
2018/10/20 PHP
实例讲解PHP验证邮箱是否合格
2019/01/28 PHP
使用PHP+Redis实现延迟任务,实现自动取消订单功能
2019/11/21 PHP
Aster vs KG BO3 第二场2.18
2021/03/10 DOTA
激活 ActiveX 控件
2006/10/09 Javascript
Prototype Selector对象学习
2009/07/23 Javascript
jquery struts 验证唯一标识(公用方法)
2013/03/27 Javascript
jquery ajax应用中iframe自适应高度问题解决方法
2014/04/12 Javascript
基于JS实现的倒计时程序实例
2015/07/24 Javascript
Bootstrap项目实战之子栏目资讯内容
2016/04/25 Javascript
jQuery+CSS3+Html5实现弹出层效果实例代码(附源码下载)
2016/05/16 Javascript
JavaScript中递归实现的方法及其区别
2017/09/12 Javascript
总结js中的一些兼容性易错的问题
2017/12/18 Javascript
使用npm安装最新版本nodejs
2018/01/18 NodeJs
jQuery动态移除与增加onclick属性的方法详解
2018/06/07 jQuery
JS简单数组排序操作示例【sort方法】
2019/05/17 Javascript
vue.js实现左边导航切换右边内容
2019/10/21 Javascript
JavaScript修改注册表实例代码
2020/01/05 Javascript
ant design vue中表格指定格式渲染方式
2020/10/28 Javascript
Node.js path模块,获取文件后缀名操作
2020/11/07 Javascript
python模拟事件触发机制详解
2018/01/19 Python
对Python Pexpect 模块的使用说明详解
2019/02/14 Python
python对指定字符串逆序的6种方法(小结)
2020/04/02 Python
python百行代码自制电脑端网速悬浮窗的实现
2020/05/12 Python
python里反向传播算法详解
2020/11/22 Python
python邮件中附加文字、html、图片、附件实现方法
2021/01/04 Python
新学期家长寄语
2014/01/19 职场文书
有兼职工作经历的简历自我评价
2014/03/07 职场文书
销售个人求职信范文
2014/04/28 职场文书
监督检查工作方案
2014/05/28 职场文书
运动会闭幕式主持词
2015/07/01 职场文书
python spilt()分隔字符串的实现示例
2021/05/21 Python
浅谈Redis缓冲区机制
2022/06/05 Redis