Tornado httpserver模块

本文介绍了Tornado的HTTPServer模块,包括HTTPServer、HTTPConnection和HTTPRequest类。HTTPServer用于创建监听套接字并处理请求,HTTPConnection负责读取和解析请求,而HTTPRequest则与请求相关。文中给出了HTTPServer单进程版本的使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       httpserver里面定义的类有HTTPServer、HTTPConnection、HTTPRequest。HTTPServer是一个非常简单的HTTP服务器,主要作用就是创建监听套接字,设置监听套接字的读事件hander,(创建多进程模式),接下来调用HTTPConnection处理整个连接。HTTPConnection读取请求并解析,调用HTTPServer设置的callback(一般是Application)处理请求,并提供发送响应的接口。这几个类都很简单。

       HTTPServer单进程版本使用如下:

http_server = httpserver.HTTPServer(handle_request)
http_server.listen(8888)     # 里面调用bind,start
ioloop.IOLoop.instance().start()
        多进程版本需要显示调用bind()和start()接口。

def bind(self, port, address=""):
        assert not self._socket
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        flags = fcntl.fcntl(self._socket.fileno(), fcntl.F_GETFD)
        flags |= fcntl.FD_CLOEXEC
        fcntl.fcntl(self._socket.fileno(), fcntl.F_SETFD, flags)
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._socket.setblocking(0)
        self._socket.bind((address, port))
        print address, port
        self._socket.listen(128)

def start(self, num_processes=1):
        assert not self._started
        self._started = True
        if num_processes is None or num_processes <= 0:
            num_processes = _cpu_count()
        if num_processes > 1 and ioloop.IOLoop.initialized():
            logging.error("...")
            num_processes = 1
        if num_processes > 1:
            logging.info("Pre-forking %d server processes", num_processes)
            for i in range(num_processes):
                if os.fork() == 0:		# hp: child process
                    import random
                    from binascii import hexlify
                    ...
		    # hp:每个子进程都有一个HTTPServer实例和IOLoop实例
		    # hp:会有惊群现象
                    self.io_loop = ioloop.IOLoop.instance()
                    self.io_loop.add_handler(
                        self._socket.fileno(), self._handle_events,
                        ioloop.IOLoop.READ)
		    # hp: child process over? No, 执行xxx.start()后面的代码
                    return

	    # hp: 父进程只等待任意一个子进程的结束么?
            os.waitpid(-1, 0)
        else:
            if not self.io_loop:
                self.io_loop = ioloop.IOLoop.instance()
			# hp: 把( listen_socket, read, handle ) 加入epoll中
            self.io_loop.add_handler(self._socket.fileno(),
                                     self._handle_events,
                                     ioloop.IOLoop.READ)
        _handle_events是监听套接字的处理函数,也很简单:

def _handle_events(self, fd, events):
        while True:
            try:
		# hp: _socket nonblocking
                connection, address = self._socket.accept()
            except socket.error, e:
                ...
            if self.ssl_options is not None:
                ...
            try:
                if self.ssl_options is not None:
                    ...
                else:
		    # hp: read and write interface of connection socekt
                    stream = iostream.IOStream(connection, io_loop=self.io_loop)
                HTTPConnection(stream, address, self.request_callback,
                               self.no_keep_alive, self.xheaders)
            except:
                ...
       这样子,连接套字的读写事件都是由HTTPConnection处理的。HTTPConnections首先调用self.stream.read_until("\r\n\r\n", self._header_callback),_header_callback函数解析请求行和请求头部,得到请求体长度Content-Lenght,接着调用self.stream.read_bytes(content_length, self._on_request_body),请求全部读完后就会调用_on_request_body函数,里面做一些解析,最后调用HTTPServer设置的request_callback( 用来产生响应,一般是Application)。  

    # hp: 读完请求头部(\r\n\r\n)执行的函数,由IOStream._read_from_buffer里面调用
    def _on_headers(self, data):
        try:
            eol = data.find("\r\n")
            start_line = data[:eol]
            try:
                method, uri, version = start_line.split(" ")
            except ValueError:
                ...
	    # hp:解析请求头部, 字典
            headers = httputil.HTTPHeaders.parse(data[eol:])
            self._request = HTTPRequest(
                connection=self, method=method, uri=uri, version=version,
                headers=headers, remote_ip=self.address[0])

            content_length = headers.get("Content-Length")
            if content_length:
                content_length = int(content_length)
                if content_length > self.stream.max_buffer_size:
                    raise _BadRequestException("Content-Length too long")

		# hp: Expect头部表示client对server有一定的要求,"100-continue"表示client
		# 先发送请求头部(不含有请求内容),判断源服务器是否接受
                if headers.get("Expect") == "100-continue":
                    self.stream.write("HTTP/1.1 100 (Continue)\r\n\r\n")

				# hp: 设置http请求读取完毕需要执行的回调函数
                self.stream.read_bytes(content_length, self._on_request_body)
                return
	
	    # hp:请求不含有请求体,表示请求全部读取完,执行HTTPServer设置的钩子(Application)
            self.request_callback(self._request)
        except _BadRequestException, e:
            ...
        发送响应时,可以调用HTTPConnection的write接口,里面直接调用IOStream的write接口,并设置发送结束的函数是_finish_request,里面判断是否是长连接。

def _finish_request(self):
        if self.no_keep_alive:
            disconnect = True
        else:
            connection_header = self._request.headers.get("Connection")
            if self._request.supports_http_1_1():
                disconnect = connection_header == "close"
            elif ("Content-Length" in self._request.headers
                    or self._request.method in ("HEAD", "GET")):
                disconnect = connection_header != "Keep-Alive"
            else:
                disconnect = True
        self._request = None
        self._request_finished = False
        if disconnect:
            self.stream.close()
            return

	# hp: 长连接,等待下一个请求
        self.stream.read_until("\r\n\r\n", self._header_callback)
       

       我用一个简单的例子测试一下:

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options

def answer( request ):
    print "invoked answer"
    res = "method:{0}\nuri:{1}\nversion:{2}\n".format(request.method,request.uri,request.version)
    for k,v in request.headers.iteritems():
        res += k + ":" + v + "\n"
    request.connection.write(res)
    request.connection.finish()

def main():
    http_server = tornado.httpserver.HTTPServer(answer, False)
    http_server.listen(8888) 
    tornado.ioloop.IOLoop.instance().start()

if __name__ == "__main__":
    main()





        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值