Tornado Code Reading-TCPServer
一个非阻塞, 单进程的 TCP
服务器
为了使用 TCPServer
, 定义一个子类改写其中的 handle_stream
方法
如果让服务器通过 SSL
传输, 如下例
1 | TCPServer(ssl_options={ |
TCPServer
simple single-process::
listen
: 监听单个进程
1 | server = TCPServer() |
bind
/start
: simple multi-process::
1 | server = TCPServer() |
如果使用start
接口, 一个 .IOLoop
不可以传入 TCPServer
结构里。 start
将总是开始服务在默认的单例 .IOLoop
add_sockets
: 高级多进程::
1 | sockets = bind_sockets(8888) |
add_sockets
接口更为复杂, 但是和tornado.process.fork_processes
使用将会更明了, add_sockets
也可以用在单进程服务中 , 如果你想要创建你的监听套节字而不是~tornado.netutil.bind_sockets
以上是怎么应用
下面看源码
初始化
1 | def __init__(self, io_loop=None, ssl_options=None, max_buffer_size=None): |
listen
在给定的端口接受连接
这个方法可能被调用多次为了监听多个端口。listen
立即生效; 之后不必调用TCPServer.start
, 但是, ,IOLoop
是必要的
1 | sockets = bind_sockets(port, address=address) |
源码里的 bind_sockets
见 bind_sockets, add_sockets
见下
add_sockets
让服务接受多个连接
sockets
参数是一个socket数组, add_sockets
一般和 tornado.process.fork_processes
联合使用 为了控制 多进程服务的启动
1 | if self.io_loop is None: |
上面的 add_accept_handler 见 add_accept_handler
add_accept_handler 第二个参数 self._handle_connection是个回调函数, 分析如下
_handle_connection在接受客户端的连接处理结束后会被调用,调用时传入连接和ioloop对象初始化 IOStream,用于对客户端的异步读写;然后调用
handle_stream
(注意这里的handle_stream 文档说了如果你只是用tcpserver
那么,handle_stream得自己重写, 如果用tornado的httpserver 那handle_stream 在 httpserver), 传入创建的IOStream
对象初始化一个HTTPConnection
,HTTPConnection
封装了IOStream
的一些操作, 用于处俩HTTPRequest
并返回。 至此HTTPServer
的创建、启动、注册回调函数过程结束
1 | #如果self.ssl_options不为空,处理ssl代码略 |
从上面的分析和源码 可知 服务器的工作流程 socket->bind->listen创建 listen socket 监听客户端, 并将每个listen socket 的 fd 注册到IOLoop的单例实例中; 当 listen socket 可读时回调 _handle_events 处理客户端请求;在与客户端通信的过程中使用 IOStream 封装读写缓冲区, 实现与客户端的异步读写。 下面我们将具体了解listen socket 的 fd 被注册到IOLoop的单例实例中 见 IOLoop
##netutil
解释:创建监听套节字绑定到给定的端口和地址
参数
- address: IP地址或主机名。如果是主机名,服务将会监听所有跟此域名有关的
IP
- Family:
socket.AF_INET
或socket.AF_INET6
- backlog: 这个参数跟
socket.listen()<socket.socket.listen>
一样 - flags:
- address: IP地址或主机名。如果是主机名,服务将会监听所有跟此域名有关的
代码
1 | sockets = [] |
add_accept_handler: 添加一个IOLoop
事件去接受新的连接在 sock
当一个连接被接受了,
callback(connection, address)
(connection
是socket对象,address
是连接的另外结尾处的地址)将会运行
1 | if io_loop is None: |