Tornado httpserver 源码简析

5 篇文章 0 订阅
3 篇文章 0 订阅
整个流程就是创建一个[b]socket[/b]
[list]
[*]socket.socket
[*]socket.bind
[*]socket.listen
[*]socket.accept
[/list]
一些值得注意的地方:
[list]
[*]sock.setblocking
[*]set_close_exec(sock.fileno())
[/list]

#!/usr/bin/env python
#-*-encoding:utf-8-*-

from tornado import httpserver
from tornado import ioloop

def handle_request(request):
message = "You requested %s\n" % request.uri
request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % (
len(message), message))
request.finish()
http_server = httpserver.HTTPServer(handle_request)#在此打断点,来看下httpserver运行的过程。
http_server.listen(8888)
ioloop.IOLoop.instance().start()


http_server = httpserver.HTTPServer(handle_request)
程序会进入HTTPServer的__init__方法中
self.request_callback = handle_request
然后
1、http_server = httpserver.HTTPServer(handle_request)

TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options,
**kwargs)

跳转到TCPServer的__init__方法
self._sockets = {} # fd -> socket object此时fd还木有哦。
此处self._sockets声明为一个dict。
2、http_server.listen(8888)
此时调用TCPServer中的listen方法

def listen(self, port, address=""):
sockets = bind_sockets(port, address=address)
self.add_sockets(sockets)

整个socket的处理被封装在一个netutil.py文件中

def bind_sockets(port, address=None, family=socket.AF_UNSPEC, backlog=128, flags=None):
sockets = []
if address == "":
address = None
if not socket.has_ipv6 and family == socket.AF_UNSPEC:
family = socket.AF_INET
if flags is None:
flags = socket.AI_PASSIVE
for res in set(socket.getaddrinfo(address, port, family, socket.SOCK_STREAM,
0, flags)):
af, socktype, proto, canonname, sockaddr = res
sock = socket.socket(af, socktype, proto)
set_close_exec(sock.fileno())
if os.name != 'nt':
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if af == socket.AF_INET6:
if hasattr(socket, "IPPROTO_IPV6"):
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
sock.setblocking(0)
sock.bind(sockaddr)
sock.listen(backlog)
sockets.append(sock)
return sockets


bind_sockets创建了一个socket
socket.socket(af, socktype, proto)
<socket object, fd=5, family=30, type=1, protocol=6>
等价于
[b]socket.socket(socket.AF_INET,socket.SOCK_STREAM,IPPROTO_TCP)[/b]
fd即socketfd,由于UNIX套接字描述符也是文件描述符,所以统称fd.
接下来调用了
[b]set_close_exec(sock.fileno())[/b]
这个方法定义在posix.py中

def set_close_exec(fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFD)
fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)


flags = fcntl.fcntl(fd, fcntl.F_GETFD)返回的flags为0;
fcntl.F_SETFD 对于fd设置文件描述符标志。此时FD_CLOEXEC = 1,即flags被置为1.
[quote]F_GETFD 对应于fd的文件描述符标志作为函数值返回。当前只定义了一个文件描述符标志FD_CLOEXEC
FD_CLOEXEC用来设置文件的close-on-exec状态标准。在exec()调用后,close-on-exec标志为0的情况,此文件不被关闭。非零则在exec()后被关闭。默认close-on-exec状态为0,需要通过FD_CLOEXEC设置。[/quote]
sock.setblocking(0)
[quote]Set blocking or non-blocking mode of the socket: if flag is 0, the socket is set to non-blocking, else to blocking mode. Initially all sockets are in blocking mode. In non-blocking mode, if a recv() call doesn’t find any data, or if a send() call can’t immediately dispose of the data, a error exception is raised; in blocking mode, the calls block until they can proceed. s.setblocking(0) is equivalent to s.settimeout(0.0); s.setblocking(1) is equivalent to s.settimeout(None).[/quote]
sock.bind(sockaddr)
sock.listen(backlog)
sockets.append(sock)
此时刚创建的socket加入到了sockets。
<socket object, fd=5, family=30, type=1, protocol=6>
<socket object, fd=6, family=2, type=1, protocol=6>
接着我们回到TCPServer中的

def listen(self, port, address=""):
sockets = bind_sockets(port, address=address)
self.add_sockets(sockets)


def add_sockets(self, sockets):
if self.io_loop is None:
self.io_loop = IOLoop.current()
for sock in sockets:
self._sockets[sock.fileno()] = sock#此处将sockets里面的socket转化为以sockfd为key socket为value 的字典。
add_accept_handler(sock, self._handle_connection,
io_loop=self.io_loop)


def add_accept_handler(sock, callback, io_loop=None):

if io_loop is None:
io_loop = IOLoop.current()

def accept_handler(fd, events):
while True:
try:
connection, address = sock.accept()#用于接收client的connect
except socket.error as e:
if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
return
raise
callback(connection, address)
io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ)

本文涉及ioloop以及iostream的地方都未介绍。
下文会继续debug ioloop以及iostream的整个流程。
参考资料:
http://www.kegel.com/c10k.html
http://kenby.iteye.com/blog/1159621
http://docs.python.org/2/library/fcntl.html
http://docs.python.org/2/library/socket.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值