基于tornado的异步TCPServer以及TCPClient

本文介绍如何利用Tornado库实现异步TCP服务端和客户端,并通过示例展示其性能优势。测试结果显示,Tornado在异步操作上表现出色,适合构建高性能网络应用。

背景

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

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

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

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

 

github地址:https://github.com/yunjianfei/tornado-async-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

 

 后续还有基于这个tcpserver的性能测试,请查看:

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

 

`tornado.tcpserver.TCPServer` 是 Tornado 框架中的一个类,用于实现 TCP 服务器的功能。它继承自 `tornado.netutil.TCPServer` 类,提供了对 TCP 连接的监听、接收和处理等功能。 `TCPServer` 的初始化方法如下: ```python class tornado.tcpserver.TCPServer(io_loop=None, ssl_options=None, max_buffer_size=None, read_chunk_size=None, max_idle_time=None, connect_timeout=None, max_body_size=None) ``` 其中,参数含义如下: - `io_loop`: 事件循环对象,用于处理 TCP 连接的事件和回调。如果不指定,将使用默认的事件循环对象。 - `ssl_options`: SSL/TLS 配置选项,用于开启加密传输。默认为 `None`,表示不开启 SSL/TLS 加密传输。 - `max_buffer_size`: 读取数据时的最大缓冲区大小,默认为 `None`,表示不限制缓冲区大小。 - `read_chunk_size`: 读取数据时的缓冲区大小,默认为 64KB。 - `max_idle_time`: 连接的最大空闲时间,超过该时间未收到数据,则关闭连接,默认为 `None`,表示不限制空闲时间。 - `connect_timeout`: 连接的最大超时时间,默认为 `None`,表示不限制超时时间。 - `max_body_size`: 读取消息体的最大大小,默认为 `None`,表示不限制消息体大小。 `TCPServer` 类还提供了以下常用方法: - `listen`: 监听指定的端口,并启动服务器。 - `stop`: 停止服务器,关闭所有连接。 - `handle_stream`: 处理新的 TCP 连接。 例如,下面的示例代码实现了一个简单的 TCP 服务器: ```python import tornado.ioloop import tornado.tcpserver import tornado.gen class MyTCPServer(tornado.tcpserver.TCPServer): async def handle_stream(self, stream, address): print(f"New connection from {address}") while True: try: data = await stream.read_until(b"\n") print(f"Received {data.decode().strip()} from {address}") await stream.write(data.upper()) except Exception as e: print(f"Error: {e}") break if __name__ == "__main__": server = MyTCPServer() server.listen(8888) tornado.ioloop.IOLoop.current().start() ``` 在上面的代码中,我们定义了一个 `MyTCPServer` 类,继承自 `tornado.tcpserver.TCPServer`。在 `handle_stream` 方法中,我们处理了新的 TCP 连接,并循环读取客户端发送的数据,并将其转换为大写后发送回客户端。最后,我们通过 `server.listen(8888)` 启动了 TCP 服务器,监听 8888 端口,并通过 `tornado.ioloop.IOLoop.current().start()` 运行了事件循环。 需要注意的是,在 `handle_stream` 方法中,我们使用了 `await` 关键字来等待客户端发送的数据,并异步地处理数据。这样可以避免在处理 TCP 连接时阻塞事件循环,提高服务器的并发性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值