Tornado httpserver 源码简析

整个流程就是创建一个 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

你可能感兴趣的:(socket,python,tornado)