使用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 try异常处理机制
Jun 01 Python
Python判断变量是否为Json格式的字符串示例
May 03 Python
利用python实现简单的循环购物车功能示例代码
Jul 05 Python
Python使用cx_Oracle调用Oracle存储过程的方法示例
Oct 07 Python
Tensorflow之构建自己的图片数据集TFrecords的方法
Feb 07 Python
Python基于生成器迭代实现的八皇后问题示例
May 23 Python
Tensorflow 实现修改张量特定元素的值方法
Jul 30 Python
python 异或加密字符串的实例
Oct 14 Python
Python如何处理大数据?3个技巧效率提升攻略(推荐)
Apr 15 Python
python3中numpy函数tile的用法详解
Dec 04 Python
Django静态资源部署404问题解决方案
May 11 Python
Django操作cookie的实现
May 26 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
东方红 - 来复式再生机的修复
2021/03/02 无线电
PHP 一个随机字符串生成代码
2010/05/26 PHP
兼容性最强的PHP生成缩略图的函数代码(修改版)
2011/01/18 PHP
PHP模拟asp.net的StringBuilder类实现方法
2015/08/08 PHP
php生成静态html页面的方法(2种方法)
2015/09/14 PHP
PHP实现路由映射到指定控制器
2016/08/13 PHP
php post json参数的传递和接收处理方法
2018/05/31 PHP
js以对象为索引的关联数组
2010/07/04 Javascript
javascript时间自动刷新实现原理与步骤
2013/01/06 Javascript
如何使用jquery控制CSS样式,并且取消Css样式(如背景色,有实例)
2013/07/09 Javascript
基于mouseout和mouseover等类似事件的冒泡问题解决方法
2013/11/18 Javascript
jquery改变tr背景色的示例代码
2013/12/28 Javascript
一个CSS+jQuery实现的放大缩小动画效果
2014/02/19 Javascript
NodeJS创建基础应用并应用模板引擎
2016/04/12 NodeJs
JS实现pasteHTML兼容ie,firefox,chrome的方法
2016/06/22 Javascript
概述如何实现一个简单的浏览器端js模块加载器
2016/12/07 Javascript
Vue通过input筛选数据
2020/10/26 Javascript
原生js实现仿window10系统日历效果的实例
2017/10/31 Javascript
JS关于刷新页面的相关总结
2018/05/09 Javascript
详解vue-cli 本地开发mock数据使用方法
2018/05/29 Javascript
[04:19]完美世界携手游戏风云打造 卡尔工作室模型介绍篇
2013/04/24 DOTA
Python批量查询域名是否被注册过
2017/06/21 Python
Python操作MongoDB数据库的方法示例
2018/01/04 Python
python方法生成txt标签文件的实例代码
2018/05/10 Python
Python给定一个句子倒序输出单词以及字母的方法
2018/12/20 Python
在Python中字典根据多项规则排序的方法
2019/01/21 Python
django框架使用orm实现批量更新数据的方法
2019/06/21 Python
python爬虫 execjs安装配置及使用
2019/07/30 Python
python将logging模块封装成单独模块并实现动态切换Level方式
2020/05/12 Python
Django如何使用asyncio协程和ThreadPoolExecutor多线程
2020/10/12 Python
大学生的应聘自我评价
2013/12/13 职场文书
义务教育学校标准化建设汇报材料
2014/08/16 职场文书
2014年客户经理工作总结
2014/11/20 职场文书
小学四年级作文之写景
2019/08/23 职场文书
详解运行Python的神器Jupyter Notebook
2021/06/03 Python
CSS 使用 resize 实现图片拖拽切换预览功能(强大功能)
2021/08/23 HTML / CSS