Tornado TCP服务器/客户端进程通信

时间:2016-10-25 12:23:37

标签: tornado

我想在许多Tornado进程之间建立通信,每个进程都充当Web服务器I.e.使用tornado.web.RequestHandler。我的想法是,我希望在进程之间建立一个完全网状的网络。我有4个流程,我想使用tornado.tcpservertornado.tcpclient建立一个持续的永久沟通:

T1---T2
| \  /| 
|  \/ |
| / \ |
T3---T4

我是TCP编程的新手但是在我在龙卷风文档中看到的示例中:http://www.tornadoweb.org/en/stable/iostream.html 在建立套接字后,对于类tornado.iostream.IOStream,在实现下,完成所有通信,然后关闭套接字。该示例通过具有回调的块来驱动代码,每个回调都执行通信任务。

但是,是否可以打开TCP连接并使BaseIOStream.read_until_close()空闲并仅在客户端写入服务器时调用?

换句话说,客户端和服务器保持连接,当客户端写入服务器时,它会以某种方式中断Tornado IOLoop来调用read()?

或者我的思维被误导了,这样做的方法是每次我需要进程进行通信时我建立一个新的TCP连接,完成工作然后终止连接?看起来每次建立这个新连接都会包含大量开销,而不是让连接保持打开状态......

2 个答案:

答案 0 :(得分:2)

这是一个基本的实现。 (我不能保证它的生产质量!)将它保存到文件并执行类似的操作,每个都在不同的终端窗口中:

> python myscript.py 10001 10002 10003
> python myscript.py 10002 10003 10001
> python myscript.py 10003 10001 10002

第一个参数是监听端口,其余参数是其他服务器的端口。

import argparse
import logging
import os
import random
import socket
import struct

from tornado import gen
from tornado.ioloop import IOLoop
from tornado.iostream import IOStream, StreamClosedError
from tornado.tcpclient import TCPClient
from tornado.tcpserver import TCPServer
from tornado.options import options as tornado_options


parser = argparse.ArgumentParser()
parser.add_argument("port", type=int, help="port to listen on")
parser.add_argument("peers", type=int, nargs="+", help="peers' ports")
opts = parser.parse_args()

# This is just to configure Tornado logging.
tornado_options.parse_command_line()
logger = logging.getLogger(os.path.basename(__file__))
logger.setLevel(logging.INFO)

# Cache this struct definition; important optimization.
int_struct = struct.Struct("<i")
_UNPACK_INT = int_struct.unpack
_PACK_INT = int_struct.pack

tcp_client = TCPClient()


@gen.coroutine
def client(port):
    while True:
        try:
            stream = yield tcp_client.connect('localhost', port)
            logging.info("Connected to %d", port)

            # Set TCP_NODELAY / disable Nagle's Algorithm.
            stream.set_nodelay(True)

            while True:
                msg = ("Hello from port %d" % opts.port).encode()
                length = _PACK_INT(len(msg))
                yield stream.write(length + msg)
                yield gen.sleep(random.random() * 10)

        except StreamClosedError as exc:
            logger.error("Error connecting to %d: %s", port, exc)
            yield gen.sleep(5)


loop = IOLoop.current()

for peer in opts.peers:
    loop.spawn_callback(client, peer)


class MyServer(TCPServer):
    @gen.coroutine
    def handle_stream(self, stream, address):
        logging.info("Connection from peer")
        try:
            while True:
                # Read 4 bytes.
                header = yield stream.read_bytes(4)

                # Convert from network order to int.
                length = _UNPACK_INT(header)[0]

                msg = yield stream.read_bytes(length)
                logger.info('"%s"' % msg.decode())

                del msg  # Dereference msg in case it's big.

        except StreamClosedError:
            logger.error("%s disconnected", address)


server = MyServer()
server.listen(opts.port)

loop.start()

请注意,我们不会调用read_until_close,因此我们需要一些方法来了解消息何时被完全接收。我在每条消息的开头用一个32位整数来编写消息其余部分的长度。

你问,&#34;当客户端写入服务器时,它会以某种方式中断Tornado IOLoop来调用read()?&#34;这就是Tornado的IOLoop所代表的,它是我们所说的&#34; async&#34;:许多Tornado协同程序或回调可以等待网络事件,IOLoop在事件发生时唤醒它们他们等待发生。无论你在哪里看到了什么,&#34;产生&#34;在上面的代码中。

答案 1 :(得分:1)

  
    

但是,是否可以打开TCP连接并使BaseIOStream.read_until_close()空闲并仅在客户端写入服务器时调用?

  

不知道龙卷风。但是,就TCP而言,一旦建立连接(服务器和客户端将状态维持为'ESTABLISHED'),服务器和客户端就可以交换数据,直到任何人希望关闭连接或发生导致消息发送的网络问题不要到达另一端。

  
    

换句话说,客户端和服务器保持连接,当客户端写入服务器时,它会以某种方式中断Tornado IOLoop来调用read()?

  

是。应该是这种情况。

  
    

或者我的想法被误导了,这样做的方法是每次我需要进程进行通信时我建立一个新的TCP连接,完成工作然后终止连接?

  

没有。每次数据交换都不需要重新启动TCP连接