tornado的http服务器实现
使用tornado实现的一个简单http服务器:只需要定义自己的处理方法,其他的东西全部交给tornado完成.
#coding:utf-8 import tornado.httpserver
import tornado.ioloop def handle_request(request):
message = "Hello World from Tornado Http Server"
request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % (len(message), message))
request.finish() http_server = tornado.httpserver.HTTPServer(handle_request)
http_server.bind(8080)
http_server.start()
#启动事件循环,开始监听网络事件,主要是socket的读和写
tornado.ioloop.IOLoop.instance().start()
1.socket、bind及listen函数(httpserver中实现)
#getaddrinfo返回服务器的所有网卡信息, 每块网卡上都要创建监听客户端的socket.
for res in socket.getaddrinfo(address, port, family, socket.SOCK_STREAM,0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG):
af, socktype, proto, canonname, sockaddr = res # 创建listen socket
sock = socket.socket(af, socktype, proto) # 设置socket的属性
flags = fcntl.fcntl(sock.fileno(), fcntl.F_GETFD)
flags |= fcntl.FD_CLOEXEC
fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, flags)
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) # bind 和 listen
sock.bind(sockaddr)
sock.listen(128) # 加入ioloop
#ioloop可以理解为一个容器,用户把socket和回调函数注册到容器中, 容器内部会轮询socket, 一旦某个socket可以读写, 就调用回调函数来处理socket的读写事件.
self._sockets[sock.fileno()] = sock
if self._started:
#监听listen_socket的读事件, 回调函数为_handle_events,一旦listen socket可读, 说明客户端请求到来, 然后调用_handle_events接受客户端的请求
self.io_loop.add_handler(sock.fileno(), self._handle_events,ioloop.IOLoop.READ)
2.accept函数(httpserver中实现)
def _handle_events(self, fd, events):
while True:
try:
#accept方法返回客户端的socket(注意connection的类型是socket), 以及客户端的地址
connection, address = self._sockets[fd].accept()
except socket.error, e:
if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
return
raise
try:
#创建IOStream对象, 用来处理socket的异步读写. 这一步会调用ioloop.add_handler把client socket加入ioloop,然后创建HTTPConnection, 处理用户的请求.
stream = iostream.IOStream(connection, io_loop=self.io_loop)
HTTPConnection(stream, address, self.request_callback,
self.no_keep_alive, self.xheaders)
except:
logging.error("Error in connection callback", exc_info=True)
3.IOStream
IOStream对socket的读写做了一层封装, 通过使用两个缓冲区, 实现对socket的异步读写.
为了实现对client socket的异步读写, 我们为client socket创建两个缓冲区: _read_buffer和_write_buffer, 写: 先写到_write_buffer, 读: 从_read_buffer读. 这样我们就不用直接读写socket, 进而实现异步读写. 这些操作都封装在IOStream类中
IOStream的初始化:IOStream与socket是一一对应的, 初始化主要做4个工作
(1) 初始化IOStream对应的socket
(2) 分配输入缓冲区_write_buffer
(3) 分配输出缓冲区_read_buffer
(4) 把socket加入ioloop, 这样当socket可读写的时候, 调用回调函数_handle_events把数据从socket读入buffer, 或者把数据从buffer发送给socket
#IOStream的__init__方法
self.socket = socket
self.io_loop = io_loop or ioloop.IOLoop.instance()
self._read_buffer = collections.deque()
self._write_buffer = collections.deque()
self.io_loop.add_handler(self.socket.fileno(),self._handle_events,self._state)
IOStream提供的接口:
(1) write(data):把数据写入IOStream的_write_buffer
(2) read_until(delimiter, callback):从_read_buffer读取数据, delimiter作为读取结束符, 完了调用callback
(3) read_bytes(num_of_bytes, callback):从_read_buffer读取指定大小的数据, 完了调用callback
异步IO的例子:一系列调用都是通过回调函数实现的,这就是异步的处理方式
#!/usr/bin/env python
# -*- coding:utf-8 -*- from tornado import ioloop
from tornado import iostream
import socket def send_request():
stream.write("GET /index.html HTTP/1.0\r\nHost: nginx.net\r\n\r\n")
#回调on_headers解析协议头
stream.read_until("\r\n\r\n", on_headers) def on_headers(data):
headers = {}
for line in data.split("\r\n"):
parts = line.split(":")
if len(parts) == 2:
headers[parts[0].strip()] = parts[1].strip()
#回调on_body把数据打印出来
stream.read_bytes(int(headers["Content-Length"]), on_body) def on_body(data):
print data
stream.close()
ioloop.IOLoop.instance().stop() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
stream = iostream.IOStream(s)
#调用connect连接服务器, 完成后回调send_request发出请求, 并读取服务器返回的http协议头
stream.connect(("nginx.net", 80), send_request)
ioloop.IOLoop.instance().start()
4.处理请求:HTTPConnection
HttpConnection类专门用来处理http请求, 处理http请求的一般流程是:
HTTPConnection实现了一系列的函数用来处理这些流程, 参见下图:
5.IOLoop:基于epoll
在Tornado服务器中, IOLoop是调度的核心模块, Tornado服务器回把所有的socket都注册到IOLoop, 注册的时候指明回调处理函数, IOLoop内部不断的监听IO事件, 一旦发现某个socket可读写, 就调用其注册时指定的回调函数.
IOLoop的结构图如下所示:
使用IOLoop的一个例子:
from tornado import ioloop
from tornado import iostream
import socket
import errno
import functools def handle_connection(client, address):
client.send("Hello World from A Simple TCP Server")
client.close()
//该函数作用是接受客户端新的连接
def connection_ready(sock, fd, events):
while True:
try:
connection, address = sock.accept()
except socket.error, e:
if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
raise
return
connection.setblocking(0)
//此处其实应该设置为异步的,将相应的套接字加入到事件循环并注册相应的回调函数,只是没这样做
handle_connection(connection, address)
//下面的代码可以直接调用httpserver或者tcpserver实现
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
//一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用
//server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
//启用套接字的非阻塞模式
sock.setblocking(0)
sock.bind(("localhost", 8080))
//128是连接队列的最大长度
sock.listen(128)
//创建一个ioloop实例
io_loop = ioloop.IOLoop.instance()
callback = functools.partial(connection_ready, sock)
//fileno将sock转换为标准的描述符,add_handler向ioloop中注册socket以及对应的回调函数、监听事件类型
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
io_loop.start()
IOLoop的单例模式:
#coding:utf-8
import os class IOLoop(object):
@classmethod
#cls和self一样,是python的built-in变量,self表示类的实例,cls表示类
#cls一般用于static method,因为static method无须实例化就可以调用,所以传递cls给static method,然后调用cls()可以创建对象
def instance(cls):
if not hasattr(cls,"_instance"):
cls._instance = cls()
return cls._instance
@classmethod
def initialized(cls):
return hasattr(cls,"_instance") def service(self):
print "hello world" print IOLoop.initialized(),
ioloop = IOLoop.instance()
ioloop.service() if os.fork() == 0:
print IOLoop.initialized(),
ioloop = IOLoop.instance()
ioloop.service()
Always use 'self' for the first argument to instance methods.
Always use 'cls' for the first argument to class methods.
tornado的http服务器实现的更多相关文章
- 深入理解Tornado——一个异步web服务器
本人的第一次翻译,转载请注明出处:http://www.cnblogs.com/yiwenshengmei/archive/2011/06/08/understanding_tornado.html原 ...
- Tornado源码分析之http服务器篇
转载自 http://kenby.iteye.com/blog/1159621 一. Tornado是什么? Facebook发布了开源网络服务器框架Tornado,该平台基于Facebook刚刚收购 ...
- Tornado WEB服务器框架 Epoll
引言: 回想Django的部署方式 以Django为代表的python web应用部署时采用wsgi协议与服务器对接(被服务器托管),而这类服务器通常都是基于多线程的,也就是说每一个网络请求服务器都会 ...
- 在 tornado 中异步无阻塞的执行耗时任务
在 tornado 中异步无阻塞的执行耗时任务 在 linux 上 tornado 是基于 epoll 的事件驱动框架,在网络事件上是无阻塞的.但是因为 tornado 自身是单线程的,所以如果我们在 ...
- tornado远远不止
大家的回答都有点片面,更多的关注web框架成,其实tornado远远不止这些,且听我慢慢到来1.高性能的网络库,这可以和gevent,twisted,libevent等做对.提供了异步io支持,超时事 ...
- Tornado框架简介
-------------------简介------------------- 1.概念: Tornado全称Tornado Web Server,是一个用Python语言写成的Web服务器 ...
- tornado httpserver
# coding:utf-8 import tornado.web import tornado.ioloop import tornado.httpserver # 新引入httpserver模块 ...
- 在 ubuntu 中安装 python3.5、 tornado、 pymysql
一.在 ubuntu 中安装 python3.5 1.首先,在系统中是自带python2.7的.不要卸载,因为一些系统的东西是需要这个的.python2.7和python3.5是可以共存的. 命令如下 ...
- PythonWEB框架之Tornado
前言 Tornado(龙卷风)和Django一样是Python中比较主流的web框架,Tornado 和现在的主流 Web 服务器框架也有着明显的区别:Tornado自带socket,并且实现了异步非 ...
随机推荐
- 从0开始搭建SQL Server 2012 AlwaysOn 第一篇(AD域与DNS)
随着业务发展,公司需要提高数据安全与性能需求,所以需要对新技术预研(先采坑),做技术积累: 了解相关AlwaysOn 故障转移集群(热备),数据路由(ICX),Moebius(莫比斯数据路由) 决定测 ...
- 中国版 Office 365 (X-Tenant / Tango) 功能验证报告 - 2 基本步骤
说明: 1. 前期准备 - 在Azure上模拟出生产环境: 包括父域域控.子域域控.父域的Exchange Server.子域的Exchange Server.对Exchange Server, 需要 ...
- 洛谷 P1364 医院设置
题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上 ...
- Net作业调度
Net作业调度(一) -Quartz.Net入门 2014-11-01 13:14 by 蘑菇先生, 13954 阅读, 7 评论, 收藏, 编辑 背景 很多时候,项目需要在不同时刻,执行一个或很多个 ...
- (转)淘淘商城系列——使用maven tomcat插件启动聚合工程
http://blog.csdn.net/yerenyuan_pku/article/details/72672389 上文我们一起学习了如何使用maven tomcat插件来启动web工程,本文我们 ...
- hibernate fetch属性
fetch的属性值有:select(默认值).join.subselect 1)当fetch=”select”时,程序会先查询返回要查询的主体对象,然后根据lazy属性看是否懒加载. 2)当fetch ...
- vue的[__ob__: Observer]
为什么会获取不到里面的值 因为:vue data 里面值都是有这个属性的.这是被vue接管的数据,observer是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭o ...
- 解决mysql时区问题以及SSL问题
看了下网上的教程,觉得都太麻烦啦,这里推荐个简单的! 解决时区问题 只需要加上serverTimezone=UTC即可,如下: spring.datasource.url=jdbc:mysql://1 ...
- js计算最小凸多边形
最近在做项目的时候遇到一个需求:要求用户可以在地图上绘制多边形,项目中使用的是高德地图,由于无法限制用户绘制的方式,可能出现下图的情况 用户期望的效果如下图所示 本质上,用户希望出现的是凸多边形而不是 ...
- JavaScript实现AES算法加密和解密
JavaScript的加密和解密用的是google的CryptoJS库.本文以AES/ECB/NoPadding为例展示AES加密和解密的方法. 需要下载CryptoJS库,下载地址如下:https: ...