基于tornado的异步TCPServer以及TCPClient

标签: tornado 异步 tcpserver | 发表时间:2015-02-25 14:59 | 作者:
出处:http://www.iteye.com

背景

关于tornado,我这里就不详细讲了,有兴趣的同学可以通过以下两篇博客了解一下:

  http://yunjianfei.iteye.com/blog/2185476

http://yunjianfei.iteye.com/blog/2185446

我们一般用tornado来编写web程序,但实际上,tornado底层的代码非常优秀,也可以用这些代码来编写TCP应用。

 

代码

tornado最突出的特点就是“异步”,所以,我这里编写了一个异步的TCPServer和一个异步的TCPClient来帮助大家理解,下面直接看代码:

 

文件:tcp_server.py

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

from tornado import ioloop, httpclient, gen
from tornado.gen import Task
from tornado.tcpserver import TCPServer
import pdb, time, logging
from tornado import stack_context
from tornado.escape import native_str

#Init logging
def init_logging():
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    sh = logging.StreamHandler()

    formatter = logging.Formatter('%(asctime)s -%(module)s:%(filename)s-L%(lineno)d-%(levelname)s: %(message)s')
    sh.setFormatter(formatter)

    logger.addHandler(sh)
    logging.info("Current log level is : %s", logging.getLevelName(logger.getEffectiveLevel()))


class MyServer(TCPServer):
    def __init__(self, io_loop=None, **kwargs):
        TCPServer.__init__(self, io_loop=io_loop, **kwargs)

    def handle_stream(self, stream, address):
        TCPConnection(stream, address, io_loop=self.io_loop)

class TCPConnection(object):
    def __init__(self, stream, address, io_loop):
        self.io_loop = io_loop
        self.stream = stream
        self.address = address
        self.address_family = stream.socket.family

        self.EOF = b' END'

        self._clear_request_state()

        self._message_callback = stack_context.wrap(self._on_message)

        self.stream.set_close_callback(self._on_connection_close)
        self.stream.read_until(self.EOF, self._message_callback)

    def _on_timeout(self):
        logging.info("Send message..")
        self.write("Hello client!" + self.EOF)

    def _on_message(self, data):
        try:
            timeout = 5
            data = native_str(data.decode('latin1'))
            logging.info("Received: %s", data)
            self.io_loop.add_timeout(self.io_loop.time() + timeout, self._on_timeout)
        except Exception, ex:
            logging.error("Exception: %s", str(ex))

    def _clear_request_state(self):
        """Clears the per-request state.
        """
        self._write_callback = None
        self._close_callback = None

    def set_close_callback(self, callback):
        """Sets a callback that will be run when the connection is closed.
        """
        self._close_callback = stack_context.wrap(callback)

    def _on_connection_close(self):
        if self._close_callback is not None:
            callback = self._close_callback
            self._close_callback = None
            callback()
        self._clear_request_state()

    def close(self):
        self.stream.close()
        # Remove this reference to self, which would otherwise cause a
        self._clear_request_state()

    def write(self, chunk, callback=None):
        """Writes a chunk of output to the stream."""
        if not self.stream.closed():
            self._write_callback = stack_context.wrap(callback)
            self.stream.write(chunk, self._on_write_complete)

    def _on_write_complete(self):
        if self._write_callback is not None:
            callback = self._write_callback
            self._write_callback = None
            callback()

def main():
    init_logging()

    server = MyServer()
    server.listen(8001)
    ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    try:
        main()
    except Exception, ex:
        print "Ocurred Exception: %s" % str(ex)
        quit()

 

文件: tcp_client.py

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

from tornado import ioloop, httpclient, gen
from tornado.gen import Task
import pdb, time, logging
import tornado.ioloop
import tornado.iostream
import socket

#Init logging
def init_logging():
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    sh = logging.StreamHandler()

    formatter = logging.Formatter('%(asctime)s -%(module)s:%(filename)s-L%(lineno)d-%(levelname)s: %(message)s')
    sh.setFormatter(formatter)

    logger.addHandler(sh)
    logging.info("Current log level is : %s", logging.getLevelName(logger.getEffectiveLevel()))


class TCPClient(object):
    def __init__(self, host, port, io_loop=None):
        self.host = host
        self.port = port
        self.io_loop = io_loop

        self.shutdown = False
        self.stream = None
        self.sock_fd = None

        self.EOF = b' END'


    def get_stream(self):
        self.sock_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.stream = tornado.iostream.IOStream(self.sock_fd)
        self.stream.set_close_callback(self.on_close)

    def connect(self):
        self.get_stream()
        self.stream.connect((self.host, self.port), self.send_message)

    def on_receive(self, data):
        logging.info("Received: %s", data)
        self.stream.close()

    def on_close(self):
        if self.shutdown:
            self.io_loop.stop()

    def send_message(self):
        logging.info("Send message....")
        self.stream.write(b"Hello Server!" + self.EOF)
        self.stream.read_until(self.EOF, self.on_receive)
        logging.info("After send....")

    def set_shutdown(self):
        self.shutdown = True

def main():
    init_logging()

    io_loop = tornado.ioloop.IOLoop.instance()
    c1 = TCPClient("127.0.0.1", 8001, io_loop)
    c2 = TCPClient("127.0.0.1", 8001, io_loop)

    c1.connect()
    c2.connect()

    c2.set_shutdown()

    logging.info("**********************start ioloop******************")
    io_loop.start()

if __name__ == "__main__":
    try:
        main()
    except Exception, ex:
        print "Ocurred Exception: %s" % str(ex)
        quit()

 

分别运行tcp_server.py和tcp_client.py,通过测试结果,可以非常明显的了解到tornado的“异步”,以及强大的性能。

 

测试结果如下:

tcp_server:

2015-02-25 15:01:13,179 -server:server.py-L22-INFO: Current log level is : DEBUG
2015-02-25 15:01:18,146 -server:server.py-L56-INFO: Received: Hello Server! END
2015-02-25 15:01:18,146 -server:server.py-L56-INFO: Received: Hello Server! END
2015-02-25 15:01:23,146 -server:server.py-L49-INFO: Send message..
2015-02-25 15:01:23,147 -server:server.py-L49-INFO: Send message..

 tcp_client:

2015-02-25 15:01:18,144 -client:client.py-L22-INFO: Current log level is : DEBUG
2015-02-25 15:01:18,145 -client:client.py-L77-INFO: **********************start ioloop******************
2015-02-25 15:01:18,145 -client:client.py-L56-INFO: Send message....
2015-02-25 15:01:18,145 -client:client.py-L59-INFO: After send....
2015-02-25 15:01:18,146 -client:client.py-L56-INFO: Send message....
2015-02-25 15:01:18,146 -client:client.py-L59-INFO: After send....
2015-02-25 15:01:23,147 -client:client.py-L48-INFO: Received: Hello client! END
2015-02-25 15:01:23,147 -client:client.py-L48-INFO: Received: Hello client! END

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [tornado 异步 tcpserver] 推荐:

基于tornado的异步TCPServer以及TCPClient

- - ITeye博客
关于tornado,我这里就不详细讲了,有兴趣的同学可以通过以下两篇博客了解一下:. 我们一般用tornado来编写web程序,但实际上,tornado底层的代码非常优秀,也可以用这些代码来编写TCP应用. tornado最突出的特点就是“异步”,所以,我这里编写了一个异步的TCPServer和一个异步的TCPClient来帮助大家理解,下面直接看代码:.

#Tornado# 文档翻译中文版

- sasiky - python.cn(jobs, news)
内容索引 Table of Contents. 4.1   请求处理和请求参数. 4.2   RequestHandler中的主要方法. 4.4   Cookies和安全Cookies. 4.6   跨站伪造请求的防范. 4.7   静态文件和主动式文件缓存. 4.10   非阻塞式的异步请求. 7   WSGI 和 Google AppEngine.

Tornado 的 IOStream 简介与应用

- Ken - python.cn(jobs, news)
Tornado的核心源码是由ioloop.py和iostream.py这2个文件组成的. 前者提供了一个循环,用于处理I/O事件;后者则封装了一个非阻塞的socket. 有了这2者后,就能搭建起TCP server和HTTP server,实现异步HTTP客户端,这便是Tornado的主要内容了. 之前在研究socket时已差不多弄懂了ioloop的逻辑,于是本文就接着研究iostream了.

Torrent Tornado:浏览器内 BT 下载

- - LinuxTOY
Torrent Tornado 是一款完全使用 JavaScript 实现的附加组件,可以为 Firefox 浏览器增加 BT 下载功能. 体积小巧(不到 100K),完全使用 JavaScript 实现,跨平台且无本地二进制依赖. 支持和磁力链接及种子文件关联. 注意 当前 1.0 版本仅支持下载,不支持上传.

用 Tornado 实现简单的在线代理

- Ken - python.cn(jobs, news)
实现代理的方式很多种,流行的web服务器也大都要代理的功能,比如http://www.tornadoweb.cn用的就是nginx的代理功能做的tornadoweb官网的镜像. 最近,我在开发一个移动运用(以下简称APP)的后台程序(Server),该运用需要调用到另一平台产品(Platform)的API.

用Tornado打造WebSocket与Ajax Long-Polling自适应聊天室

- satan - keakon的涂鸦馆
这几天忙着研究Tornado,想着总得学以致用吧,于是就决定做个聊天室玩玩. 实际上在Tornado的源码里就有chat和websocket这2个聊天室的demo,分别采用Ajax Long-Polling和WebSocket技术构建. 而我要实现的则很简单:将这2种技术融合在一起. 就技术而言,WebSocket的通信开销很少,没有连接数的限制,但由于本身比较新,支持它的浏览器并不多(目前仅有Chrome 6+、Safari 5.0.1+、Firefox 4+和Opera 11+,且Firefox和Opera还因安全原因默认禁用了).

Flask, Tornado, GEvent, 以及它们的结合的性能比较

- Ken - python.cn(jobs, news)
英文: http://blog.wensheng.com/2011/10/performance-of-flask-tornado-gevent-and.html. 我在选一个python的互联网框架, 本来已经定下来用Tornado了.  但我还听到很多人推荐Flask的简单性和灵活性, 还有gevent的高性能, 所以决定也试试它们以及它们和Tornado的结合.

Browser和Server持续同步的几种方式(jQuery+tornado演示)

- mrluanma - 残阳似血的博客
在B/S模型的Web应用中,客户端常常需要保持和服务器的持续更新. 这种对及时性要求比较高的应用比如:股票价格的查询,实时的商品价格,自动更新的twitter timeline以及基于浏览器的聊天系统(如GTalk)等等. 由于近些年AJAX技术的兴起,也出现了多种实现方式. 本文将对这几种方式进行说明,并用jQuery+tornado进行演示,需要说明的是,如果对tornado不了解也没有任何问题,由于tornado的代码非常清晰且易懂,选择tornado是因为其是一个非阻塞的(Non-blocking IO)异步框架(本文使用2.0版本).

linux异步IO浅析

- Sepher - kouu&#39;s home
知道异步IO已经很久了,但是直到最近,才真正用它来解决一下实际问题(在一个CPU密集型的应用中,有一些需要处理的数据可能放在磁盘上. 预先知道这些数据的位置,所以预先发起异步IO读请求. 等到真正需要用到这些数据的时候,再等待异步IO完成. 使用了异步IO,在发起IO请求到实际使用数据这段时间内,程序还可以继续做其他事情).

Android handler异步更新

- - 博客园_首页
private static final int MSG_SUCCESS = 0;// 获取图片成功的标识. private static final int MSG_FAILURE = 1;// 获取图片失败的标识. mImageView.setImageBitmap((Bitmap) msg.obj);// imageview显示从网络获取到的logo.