Tornado
一、Tornado初探
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): # self.write("Hello, world") self.render('index.html') class LoginHandler(tornado.web.RequestHandler): def get(self): self.render('login.html') def post(self, *args, **kwargs): username = self.get_argument('username') print(username) self.redirect('/index') settings = { 'template_path':'templates', 'static_path':'statics', 'static_url_prefix':'static', } #application对象中封装了 路由信息,配置信息 application = tornado.web.Application([ (r"/index", MainHandler), (r'/login.html',LoginHandler), ],**settings) # application.add_handlers(r"^a\.com$",[ # (r"/", MainHandler), # (r'/login.html',LoginHandler), # ]) if __name__ == "__main__": #创建socket对象 [socket,] #socket = socket.socket application.listen(8888) #开启r,w,e = select.select([socket,],) tornado.ioloop.IOLoop.instance().start()
二、Tornado原理
1.基本操作
-路由系统(用户将请求发送给引擎,通过路由系统发送给视图函数)
url - 类(根据method执行方法)
-视图函数(由视图函数渲染模板返回用户)
-控制器
class Foo(xx): def get(self): self.write() self.render() self.redirect() self.get_argument() self.get_cookies() self.set_cookies() self.get_body_argument() post方式 self.get_query_argument() get方式 self.get_argument() get或post方式 self.get_arguments() self.get_cookie() self.set_cookie() self.get_secure_cookie('xxxx') self.set_secure_cookie('xxxx','oooo') PS:加密cookie依赖配置文件cookie_secret self.request.files['filename'] self._headers 查找请求数据:1、Handler对象
2、self.request -> tornado.httputil.HTTPServerRequest
def post(self): pass -模板引擎(更接近python) -基本 {{li[0]}} {% for i in range(10) %} {% end %} -UIMethod -内容 -UIModule -css,js -内容
2.自定义开源组件
-session (1).super按照顺序查找 (2).类名.方法名(self) class Bar(object): def __init__(self): pass def __setitem__(self, key, value): pass def __getitem__(self, item): pass def __delitem__(self, key): pass (3)self是谁?永远是调用方法的对象 -Form表单验证(*)
# -*- coding: utf-8 -*-
"""
@Datetime: 2018/8/28
@Author: Zhang Yafei
"""
import tornado.web
import tornado.ioloop
import time
import hashlib
container = {}
class Cache(object):
"""将session保存在内存"""
def __contains__(self, item):
return item in container
def initial(self,random_str):
container[random_str] = {}
def get(self,random_str,key):
return container[random_str].get(key)
def set(self, random_str,key,value):
container[random_str][key] = value
print(container)
def delete(self, random_str,key):
del container[random_str][key]
def clear(self,random_str):
del container[random_str]
def open(self):
pass
def close(self):
pass
P = Cache
class Bar(object):
def __init__(self,handler):
self.handler = handler
self.random_str = None
self.db = P()
self.db.open()
#去用户请求信息中获取session_id,如果没有,新用户
client_random_str = self.handler.get_cookie('session_id')
if not client_random_str:
"""新用户"""
self.random_str = self.create_random_str()
self.db.initial(self.random_str)
else:
if client_random_str in self.db:
"""老用户"""
self.random_str = client_random_str
else:
"""非法用户"""
self.random_str = self.create_random_str()
self.db.initial(self.random_str)
#更新cookie过期时间
self.handler.set_cookie('session_id', self.random_str, expires=time.time()+1800)
self.db.close()
def create_random_str(self):
v = str(time.time())
m = hashlib.md5()
m.update(v.encode('utf-8'))
return m.hexdigest()
def __setitem__(self, key, value):
self.db.open()
self.db.set(self.random_str, key, value)
self.db.close()
"""
# 1.生成随机字符串
if self.random_str:
pass
else:
self.random_str = self.create_random_str()
# 2.写入用户cookies(只写一次)
self.handler.set_cookie('seesion_id',self.random_str)
# 3.后台存储
if self.random_str in container:
container[self.random_str][key] = value
else:
container[self.random_str]={}
container[self.random_str][key] = value
"""
def __getitem__(self, key):
self.db.open()
v = self.db.get(self.random_str, key)
self.db.close()
return v
def __delitem__(self, key):
self.db.open()
self.db.delete(self.random_str,key)
self.db.close()
def clear(self):
self.db.open()
self.db.clear(self.random_str)
self.db.close()
class Foo(object):
def initialize(self):
#self是MainHandler对象
self.session = Bar(self)
# super(Foo, self).initialize()
"""python支持多继承"""
class HomeHandler(Foo,tornado.web.RequestHandler):
def get(self):
user = self.session['uuuuu']
if not user:
self.redirect('http://www.oldboyedu.com')
else:
self.write(user)
class LoginHandler(Foo,tornado.web.RequestHandler):
def get(self):
self.session['uuuuu'] = 'root'
self.redirect('/home')
"""
# self.set_cookie()
#1.生成随机字符串
#2.写入用户cookies
#3.后台存储
self.session['is_login'] = True #__setitem__
self.session['user'] = 'zhangyafei' #__setitem__
# self.session['xx'] #__getitem__
# del self.session['xx'] #__delitem__
self.write('hello world')
"""
application = tornado.web.Application([
(r'/login', LoginHandler),
(r'/home',HomeHandler),
])
if __name__ == '__main__':
application.listen(9999)
tornado.ioloop.IOLoop.instance().start()
三、websocket
概述
--http,socket实现,短连接,单工,请求响应
--WebSocket,socket实现,双工通道,,请求响应,推送 魔法字符串
websocket原理
-服务端(socket服务器) 1.服务器开启socket,监听ip和端口 3.允许连接 * 5.服务端收到特殊值【特殊值加密sha1,migic string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"】 * 6.加密后的值发送给客户端 -客户端(浏览器) 2.客户端发起连接请求(ip和端口) * 4.客户端生成一个随机字符串xxx,【特殊值加密sha1,migic string="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"】,向服务端发送一段特殊值 * 7.客户端收到加密的值
""" 1.位运算,右移动 10010001 右4 00001001 左4 100100010000 2.异或运算 1,1:1 0,1 :0 0,0 :0 查看某个数的后四位 10001010 00001111 00001010 info = b'\x81\x8b\xc9\x11\x81b\xa1t\xed\x0e\xa61\xf6\r\xbb}\xe5' opcode = info[0] & 15 获取第一个字节的后四位 fin = info[0] >> 7 payload_len = info[1] & 127 if payload_len<126: 头部占2个字节 elif payload_len == 126: 头部占4个字节 else: 头部占10个字节 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +------------------------------------------------------------ """
四、异步非阻塞web框架
同步阻塞
# -*- coding: utf-8 -*- """ @Datetime: 2018/8/29 @Author: Zhang Yafei """ import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): import time time.sleep(5) self.write("Hello, world") class IndexHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/main", MainHandler), (r"/index", IndexHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
异步非阻塞
# -*- coding: utf-8 -*- """ @Datetime: 2018/8/29 @Author: Zhang Yafei """ import time import tornado.ioloop import tornado.web from tornado import gen from tornado.concurrent import Future class MainHandler(tornado.web.RequestHandler): @gen.coroutine def get(self): future = Future() #特殊的形式等待5秒 tornado.ioloop.IOLoop.current().add_timeout(time.time()+5,self.doing) yield future def doing(self,*args,**kwargs): self.write('Main') self.finish() class IndexHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/main", MainHandler), (r"/index", IndexHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
自定义异步非阻塞框架
支持异步非阻塞web框架 -Tornado,Nodejs:一个线程处理多个请求(服务端)
异步IO模块(socket时做的):一个线程发送多个请求(客户端)
异步IO模块:我们作为客户端向服务端发起并发请求,select监听socket是否已经发生变化
futrue对象:
future = Future()
三种请求:超时,发起请求,设置值释放挂起请求
1.挂起当前请求,线程可以处理其他请求 *****
2.future设置值,当前挂起的请求返回
自定义web框架:select + socket
--同步
--异步:通过future对象,如果future对象为None,则挂起,如果有值的话则断开连接
案例:web聊天室
# -*- coding: utf-8 -*- """ @Datetime: 2018/8/28 @Author: Zhang Yafei """ import json import uuid import tornado.web import tornado.ioloop import tornado.websocket class IndexHandler(tornado.web.RequestHandler): def get(self, *args, **kwargs): self.render('index.html') class ChatHandler(tornado.websocket.WebSocketHandler): users = set() messages = [] def open(self, *args, **kwargs): """ 客户端和服务器已经建立连接 1.连接 2.握手 :param args: :param kwargs: :return: """ ChatHandler.users.add(self) # uid = str(uuid.uuid4()) # self.write_message(uid) # for msg in ChatHandler.messages: # content = self.render_string('message.html', **msg) # self.write_message(content) def on_message(self, message): msg = json.loads(message) ChatHandler.messages.append(message) for client in ChatHandler.users: content = self.render_string('message.html',**msg) client.write_message(content) def on_close(self): """ 客户端主动关闭连接 :return: """ ChatHandler.users.remove(self) def run(): settings = { 'template_path': 'templates', 'static_path': 'static', } application = tornado.web.Application([ (r"/", IndexHandler), (r"/chat", ChatHandler), ], **settings) application.listen(8888) tornado.ioloop.IOLoop.instance().start() if __name__ == "__main__": run()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> body{ background-color: black; } .container{ border:2px solid #dddddd; height: 400px; overflow: auto; } </style> </head> <body> <div style="width: 750px;margin: 0 auto;background-color: #dddddd"> <h1 style="background-color: lawngreen">1024聊天室</h1> <div class="container"></div> <div class="input"> <input class="name" style="width: 50px;height: 15px;" type="text" value="游客"> <input type="text" id="txt"> <input type="button" id="btn" value="发送" onclick="sendMessage()"> <input type="button" id="close" value="关闭连接" onclick="closeConn()"/> </div> <div id="content"></div> </div> <script src="/static/jquery-2.1.4.min.js"></script> <script> $(function () { wsUpdater.start(); }); var wsUpdater = { socket: null, name: null, start: function() { var url = "ws://127.0.0.1:8888/chat"; wsUpdater.socket = new WebSocket(url); wsUpdater.socket.onopen = function () { /* 与服务器端连接成功后,自动执行 */ var newTag = document.createElement('div'); newTag.innerHTML = "【连接成功】"; document.getElementById('content').appendChild(newTag); }; wsUpdater.socket.onmessage = function(event) { console.log(event); if(wsUpdater.name){ wsUpdater.showMessage(event.data); }else{ wsUpdater.name = event.data; } } }, showMessage: function(content) { $('.container').append(content); } }; function closeConn() { /* 服务器端主动断开连接时,自动执行 */ if (confirm("您确定要关闭本页吗?")){ window.opener=null; window.open('','_self'); window.close(); } else{} }; $("#txt").keydown(function (e) {//当按下按键时 if (e.which == 13) {//.which属性判断按下的是哪个键,回车键的键位序号为13 $('#btn').trigger("click");//触发搜索按钮的点击事件 } }); function sendMessage() { var msg = { name:$(".name").val(), message: $("#txt").val() }; wsUpdater.socket.send(JSON.stringify(msg)); } </script> </body> </html>