使用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代码实现
Aug 17 Python
Python基于正则表达式实现检查文件内容的方法【文件检索】
Aug 30 Python
全面分析Python的优点和缺点
Feb 07 Python
python实现数据库跨服务器迁移
Apr 12 Python
详解Django rest_framework实现RESTful API
May 24 Python
《与孩子一起学编程》python自测题
May 27 Python
对pandas的算术运算和数据对齐实例详解
Dec 22 Python
pyqt5 从本地选择图片 并显示在label上的实例
Jun 13 Python
Python中if有多个条件处理方法
Feb 26 Python
Django框架实现在线考试系统的示例代码
Nov 30 Python
python 6种方法实现单例模式
Dec 15 Python
python实现ROA算子边缘检测算法
Apr 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
discuz authcode 经典php加密解密函数解析
2020/07/12 PHP
php制作unicode解码工具(unicode编码转换器)代码分享
2013/12/24 PHP
非常实用的php弹出错误警告函数扩展性强
2014/01/17 PHP
html静态页面调用php文件的方法
2014/11/13 PHP
php生成curl命令行的方法
2015/12/14 PHP
微信企业转账之入口类分装php代码
2018/10/01 PHP
表单提交验证类
2006/07/14 Javascript
仿163填写邮件地址自动显示下拉(无优化)
2008/11/05 Javascript
比较全的JS checkbox全选、取消全选、删除功能代码
2008/12/19 Javascript
jQuery根据纬度经度查看地图处理程序
2013/05/08 Javascript
jQuery的$.proxy()应用示例介绍
2014/04/03 Javascript
Lua表达式和控制结构学习笔记
2014/12/15 Javascript
使用纯javascript实现放大镜效果
2015/03/18 Javascript
Javascript中的包装类型介绍
2015/04/02 Javascript
js贪吃蛇游戏实现思路和源码
2016/04/14 Javascript
ES5学习教程之Array对象
2017/04/01 Javascript
Vuejs实现购物车功能
2017/11/05 Javascript
Node.js上传文件功能之服务端如何获取文件上传进度
2018/02/05 Javascript
Vue使用watch监听一个对象中的属性的实现方法
2019/05/10 Javascript
JavaScript实现手机号码 3-4-4格式并控制新增和删除时光标的位置
2020/06/02 Javascript
python Django批量导入数据
2016/03/25 Python
python实现多线程的方式及多条命令并发执行
2016/06/07 Python
Python回文字符串及回文数字判定功能示例
2018/03/20 Python
python 获取一个值在某个区间的指定倍数的值方法
2018/11/12 Python
python语言基本语句用法总结
2019/06/11 Python
python障碍式期权定价公式
2019/07/19 Python
python实现将视频按帧读取到自定义目录
2019/12/10 Python
AUC计算方法与Python实现代码
2020/02/28 Python
css 元素选择器的简单实例
2016/05/23 HTML / CSS
英国豪华真皮和布艺沙发销售网站:Darlings of Chelsea
2018/01/05 全球购物
搬家公司的创业计划书
2014/01/01 职场文书
物流管理专业自荐信
2014/06/23 职场文书
个园导游词
2015/02/04 职场文书
大学生个人总结范文
2015/02/15 职场文书
go开发alertmanger实现钉钉报警
2021/07/16 Golang
Python中的tkinter库简单案例详解
2022/01/22 Python