使用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在linux中输出带颜色的文字的方法
Jun 19 Python
python实现逆波兰计算表达式实例详解
May 06 Python
详解字典树Trie结构及其Python代码实现
Jun 03 Python
Django Rest framework权限的详细用法
Jul 25 Python
numpy实现神经网络反向传播算法的步骤
Dec 24 Python
解决pycharm同一目录下无法import其他文件
Feb 12 Python
tensorflow将图片保存为tfrecord和tfrecord的读取方式
Feb 17 Python
python中threading开启关闭线程操作
May 02 Python
python openCV实现摄像头获取人脸图片
Aug 20 Python
Python自动登录QQ的实现示例
Aug 28 Python
社区版pycharm创建django项目的方法(pycharm的newproject左侧没有项目选项)
Sep 23 Python
Python requests HTTP验证登录实现流程
Nov 05 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初学者写及时补给skype用户充话费的小程序
2008/11/02 PHP
php木马webshell扫描器代码
2012/01/25 PHP
php计算数组不为空元素个数的方法
2014/01/27 PHP
php操作MongoDB基础教程(连接、新增、修改、删除、查询)
2014/03/25 PHP
ThinkPHP之N方法实例详解
2014/06/20 PHP
Laravel 5框架学习之向视图传送数据(进阶篇)
2015/04/08 PHP
实例简介PHP的一些高级面向对象编程的特性
2015/11/27 PHP
PHP页面输出时js设置input框的选中值
2016/09/30 PHP
thinkphp5实现微信扫码支付
2019/12/23 PHP
javascript 基础篇1 什么是js 建立第一个js程序
2012/03/14 Javascript
JavaScript动态创建div属性和样式示例代码
2013/10/09 Javascript
js拼接html注意问题示例探讨
2014/07/14 Javascript
js 动态修改css文件的方法
2014/08/05 Javascript
JavaScript中匿名函数用法实例
2015/03/23 Javascript
Javascript显示和隐藏ul列表的方法
2015/07/15 Javascript
Javascript中的几种继承方式对比分析
2016/03/22 Javascript
简介BootStrap model弹出框的使用
2016/04/27 Javascript
Javascript json object 与string 相互转换的简单实现
2016/09/27 Javascript
Vue2.0组件间数据传递示例
2017/03/07 Javascript
浅析webpack 如何优雅的使用tree-shaking(摇树优化)
2017/08/16 Javascript
jQuery动态添加.active 实现导航效果代码思路详解
2017/08/29 jQuery
详解使用angular框架离线你的应用(pwa指南)
2019/01/31 Javascript
JavaScript代码压缩工具UglifyJS和Google Closure Compiler的基本用法
2020/04/13 Javascript
JS字符串补全方法padStart()和padEnd()
2020/05/27 Javascript
VUE : vue-cli中去掉路由中的井号#操作
2020/09/04 Javascript
vc6编写python扩展的方法分享
2014/01/17 Python
解决python写入mysql中datetime类型遇到的问题
2018/06/21 Python
python 常见的排序算法实现汇总
2020/08/21 Python
python如何实现word批量转HTML
2020/09/30 Python
python中用Scrapy实现定时爬虫的实例讲解
2021/01/18 Python
HTML5 Blob对象的具体使用
2020/05/22 HTML / CSS
世界顶级足球门票网站:Live Football Tickets
2017/10/14 全球购物
韩国商务邀请函
2014/01/14 职场文书
2014年计划生育协会工作总结
2014/11/14 职场文书
python多次执行绘制条形图
2022/04/20 Python
Nginx 常用配置
2022/05/15 Servers