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()
自定义sessIon组件

三、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()
app.py
<!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>
index.html

 

posted @ 2018-12-13 18:01  DreamBoy_张亚飞  阅读(177)  评论(0编辑  收藏  举报