tornadoのIOLoopはtcp通信用

4974 ワード

以前は元のsocketを使ってadd_handlerはfdに参加してioloopの傍受に行って、実はtornadoはもっと高級な抽象を提供して、TCPServerとIOStream
サーバコード
#! /usr/bin/env python
#coding=utf-8
  
from tornado.tcpserver import TCPServer  
from tornado.ioloop  import IOLoop  
  
class Connection(object):  
    clients = set()  
    def __init__(self, stream, address): 
        Connection.clients.add(self) 
        self._stream = stream  
        self._address = address  
        self._stream.set_close_callback(self.on_close)  
        self.read_message()  
        print "A new user has entered the chat room.", address 
      
    def read_message(self):  
        self._stream.read_until('
', self.broadcast_messages)          def broadcast_messages(self, data):           print "User said:", data[:-1], self._address         for conn in Connection.clients:               conn.send_message(data)           self.read_message()              def send_message(self, data):           self._stream.write(data)                 def on_close(self):           print "A user has left the chat room.", self._address         Connection.clients.remove(self)      class ChatServer(TCPServer):       def handle_stream(self, stream, address):          """ , , stream(IOStream ,  fd) address,          IOStream         """         print "New connection :", address, stream          Connection(stream, address)          print "connection num is:", len(Connection.clients)    if __name__ == '__main__':       print "Server start ......"       server = ChatServer()       server.listen(8000)       IOLoop.instance().start()

-----------------------------------------------------------------------------------------------
tornadoのIOLoopは、twistedよりもtcpサービスの性能が高いというioloopを参照する.pyリスニングepoll events
# Our events map exactly to the epoll events <IOLoop.XXXX>)
NONE = 0
READ = _EPOLLIN
WRITE = _EPOLLOUT
ERROR = _EPOLLERR | _EPOLLHUP

tcpネットワークプログラミング用の3つのインタフェース
def add_handler(self, fd, handler, events):
    """Registers the given handler to receive the given events for fd.

    The ``events`` argument is a bitwise or of the constants
    ``IOLoop.READ``, ``IOLoop.WRITE``, and ``IOLoop.ERROR``.

    When an event occurs, ``handler(fd, events)`` will be run.
    """
    raise NotImplementedError()

def update_handler(self, fd, events):
    """Changes the events we listen for fd."""
    raise NotImplementedError()

def remove_handler(self, fd):
    """Stop listening for events on fd."""
    raise NotImplementedError()

次はecho serverです
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import Queue
import socket

from functools import partial
from tornado.ioloop import IOLoop

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #         
sock.setblocking(0)              #  socket      

server_address = ("127.0.0.1", 9999)

sock.bind(server_address)
sock.listen(20)

fd_map = {}              #       socket   
message_queue_map = {}   # socket        

fd = sock.fileno()
fd_map[fd] = sock

ioloop = IOLoop.instance()

def handle_client(fd, event, cli_addr):
    s = fd_map[fd]
    if event & IOLoop.READ:
        data = s.recv(1024)
        if data:
            print "Received %r from %s" % (data, cli_addr)
            #            ,          
            ioloop.update_handler(fd, IOLoop.WRITE)
            message_queue_map[s].put(data)
        else:
            print "Closing %s" % cli_addr
            ioloop.remove_handler(fd)
            s.close()
            del message_queue_map[s]

    if event & IOLoop.WRITE:
        try:
            next_msg = message_queue_map[s].get_nowait()
        except Queue.Empty:
            print "%s queue empty" % cli_addr
            ioloop.update_handler(fd, IOLoop.READ)
        else:
            print "Sending %s to %s" % (next_msg, cli_addr)
            s.send(next_msg)

    if event & IOLoop.ERROR:
        print "Exception on %s" % cli_addr
        ioloop.remove_handler(fd)
        s.close()
        del message_queue_map[s]

""" socket  ioloop       READ(      )    ,        """
def handle_server(fd, event):
    s = fd_map[fd]
    if event & IOLoop.READ:
        conn, cli_addr = s.accept()
        conn.setblocking(0)
        conn_fd = conn.fileno()
        fd_map[conn_fd] = conn
        handle = partial(handle_client, cli_addr="%s:%d" % (cli_addr))   #  cli_addr      
        #     handle          tornado ioloop
        ioloop.add_handler(conn_fd, handle, IOLoop.READ)
        message_queue_map[conn] = Queue.Queue()   #          


ioloop.add_handler(fd, handle_server, IOLoop.READ)

ioloop.start()