欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tornado httpserver 源码简析

程序员文章站 2022-06-14 17:35:03
...
整个流程就是创建一个socket
  • socket.socket
  • socket.bind
  • socket.listen
  • socket.accept

一些值得注意的地方:
  • sock.setblocking
  • set_close_exec(sock.fileno())

#!/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>
等价于
socket.socket(socket.AF_INET,socket.SOCK_STREAM,IPPROTO_TCP)
fd即socketfd,由于UNIX套接字描述符也是文件描述符,所以统称fd.
接下来调用了
set_close_exec(sock.fileno())
这个方法定义在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.
引用
F_GETFD 对应于fd的文件描述符标志作为函数值返回。当前只定义了一个文件描述符标志FD_CLOEXEC
FD_CLOEXEC用来设置文件的close-on-exec状态标准。在exec()调用后,close-on-exec标志为0的情况,此文件不被关闭。非零则在exec()后被关闭。默认close-on-exec状态为0,需要通过FD_CLOEXEC设置。

sock.setblocking(0)
引用
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).

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