一、tornado介绍

  1. 轻量级web框架

  2. 异步非阻塞IO处理方式

  3. 出色的抗负载能力

  4. 优异的处理性能,不依赖多进程/多线程,一定程度上解决GIL问题

  5. WSGI全栈替代产品,推荐同时使用其web框架和HTTP服务器

  6. socket连接建立过程

  7. WSGI把应用(Application)和服务器(Server)结合起来,Tornado既可以是WSGI应用也可以是WSGI服务。
    既是WebServer也是WebFramework.

  8. tornado生命周期

二、初步简单使用

  1. httpserver形式

    from tornado import web  # tornado的基础web框架模块
    from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    from tornado import httpserver# 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(web.RequestHandler):"""处理get请求"""def get(self, *args, **kwargs):self.write("hello world")  # 做出响应数据if __name__ == '__main__':app = web.Application([(r"/", IndexHandler)])# 实例化http服务对象http_server = httpserver.HTTPServer(app)# 绑定IP和端口http_server.listen(8000)ioloop.IOLoop.current().start()
    
  2. 多进程形式

    from tornado import web  # tornado的基础web框架模块
    from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    from tornado import httpserver# 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(web.RequestHandler):"""处理get请求"""def get(self, *args, **kwargs):self.write("hello world")  # 做出响应数据if __name__ == '__main__':app = web.Application([(r"/", IndexHandler)])# 实例化http服务对象http_server = httpserver.HTTPServer(app)# 启动多个进程, 将服务器绑定到指定的端口http_server.bind(8000)http_server.start(5)  # 开启5个进程,不写默认启动一个进程"""多进程存在问题:1.每个子进程都会从父进程中复制一份IOLoop实例,如果在创建子进程前修改了IOLoop,会影响所有子进程2.所有的进程都是由一个命令启动的,无法做到在不停止服务的情况下修改代码3.所有进程共享一个端口,想要分别监控很困难"""ioloop.IOLoop.current().start()
    
  3. 常用形式

    import tornado.web  # tornado的基础web框架模块
    import tornado.ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue# 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(tornado.web.RequestHandler):"""处理get请求"""def get(self, *args, **kwargs):self.write("hello world")  # 做出响应数据if __name__ == '__main__':"""实例化一个app对象    Application:是tornado web框架的核心应用类,是与服务器对应的接口。里面保存了路由映射表,有一个listen方法用来创建一个http服务的实例,并绑定了端口"""app = tornado.web.Application([(r"/", IndexHandler)])app.listen(8000)"""IOLoop.current():返回当前线程IOLoop实例IOLoop.start():起动IOLoop实例的I/O循环,同时开启了监听"""tornado.ioloop.IOLoop.current().start()
    
  4. 带options使用形式(三种使用方式)

    server_options.py

    from tornado import web  # tornado的基础web框架模块
    from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
    from tornado import httpserver
    from tornado import options
    import config"""
    定义变量
    options.define(name="port",  # 变量名,唯一性default=8000,  # 设置默认值type=int,  # 数据类型help=None,  # 选项变量的提示信息metavar=None,  #multiple=False,  # 设置选项变量是否可以为多个值group=None,callback=None
    )
    options.options: 全局的options对象,所有定义的选项变量都会作为改对象的属性
    """# options.define(name="port", type=int, default=8000)  # (第一、二种方式)
    # options.define(name="list", type=str, default=[], multiple=True)  # (第一、二种方式)# 相当于Django中的视图
    # 一个业务处理类
    class IndexHandler(web.RequestHandler):"""处理get请求"""def get(self, *args, **kwargs):self.write("hello world")  # 做出响应数据if __name__ == '__main__':# options.options.logging = None  # 关闭日志# options.parse_command_line()  # 转换命令行参数,并保存到options.options(第一种方式)# options.parse_config_file("config")  # 去配置文件中取参数(第二种方式.txt文档)# print(options.options.list)  #(第二种方式)print(config.options.get("list"))  # 直接去py文件中取,(第三种方式)app = web.Application([(r"/", IndexHandler)])http_server = httpserver.HTTPServer(app)# 绑定IP和端口# http_server.listen(options.options.port)  # (第一种方式)# http_server.bind(options.options.port)  # 第二种方式# http_server.start(1) # 第二种方式http_server.bind(config.options.get("port"))  # 第三种方式http_server.start(1)  # 第三种方式ioloop.IOLoop.current().start()"""
    第一种方式必须在终端启动:例如:python server_options.py --port=9000 --list=good,nice,cool
    """
    

    config.txt(方式二)

    port = 7000
    list = ["good", "nice", "well"]
    

    config.py(方式三)

    options = {"port": 7000,"list": ["good", "nice", "well"]
    }
    

三、异步IO

  1. 协程实现异步IO

    import time
    import threading# 版本一
    gen = Nonedef longIo():def run():global genprint("开始延时操作。。。")time.sleep(5)try:gen.send("我发送的数据")except StopIteration as e:passprint("结束延时操作。。。")threading.Thread(target=run).start()def genCoroutine(func):def wrapper(*args, **kwargs):global gengen = func(*args, **kwargs)next(gen)return wrapper@genCoroutine
    def reaA():print("开始执行reaA")res = yield longIo()print("接收的数据", res)print("结束执行reaA")def reaB():print("开始执行reaB")time.sleep(2)print("结束执行reaA")def main():reaA()reaB()main()# 版本二
    def longIo():print("开始延时操作。。。")time.sleep(5)print("结束延时操作。。。")yield "我发送的数据"def genCoroutine(func):def wrapper(*args, **kwargs):gen1 = func(*args, **kwargs)  # reqA生成器gen2 = next(gen1)  # longIo的生成器def run(g):res = next(g)  # 拿到longIo的返回数据try:gen1.send(res)  # 返回给reqA数据except Exception as e:passthreading.Thread(target=run, args=(gen2,)).start()return wrapper@genCoroutine
    def reaA():print("开始执行reaA")res = yield longIo()print("接收的数据", res)print("结束执行reaA")def reaB():print("开始执行reaB")time.sleep(2)print("结束执行reaA")def main():reaA()reaB()main()
    

    输出结果:

    开始执行reaA
    开始延时操作。。。
    开始执行reaB
    结束执行reaA
    结束延时操作。。。
    接收的数据 我发送的数据
    结束执行reaA

  2. 异步IO加回调函数

    import time
    import threadingdef longIo(callback):def run(cb):print("开始延时操作。。。")time.sleep(5)print("结束延时操作。。。")cb("回调函数接收值")threading.Thread(target=run, args=(callback, )).start()def finsh(data):"""回调函数:param data::return:"""print("接收到longIo的值", data)def reaA():print("开始执行reaA")longIo(finsh)print("结束执行reaA")def reaB():print("开始执行reaB")longIo(finsh)print("结束执行reaB")def reaC():print("开始执行reaC")longIo(finsh)print("结束执行reaC")reaA()
    reaB()
    reaC()
    

    输出结果:

    开始执行reaA
    开始延时操作。。。
    结束执行reaA
    开始执行reaB
    开始延时操作。。。
    结束执行reaB
    开始执行reaC
    开始延时操作。。。
    结束执行reaC
    结束延时操作。。。
    结束延时操作。。。
    接收到longIo的值 回调函数接收值
    结束延时操作。。。
    接收到longIo的值 回调函数接收值
    接收到longIo的值 回调函数接收值

四、tornado基本框架结构

mysql_db.py

import pymysql# 单例模式的装饰器
# def singleton(cls, *args, **kwargs):
#     instance = {}
#
#     def _singleton():
#         if cls not in instance:
#             instance[cls] = cls(*args, **kwargs)
#         return instance[cls]
#     return _singletonclass LongMySQl(object):def __init__(self, host, user, passwd, dbName):self.host = hostself.user = userself.passwd = passwdself.dbName = dbNamedef connect(self):self.db = pymysql.connect(self.host, self.user, self.passwd, self.dbName)self.cursor = self.db.cursor(cursor=pymysql.cursors.DictCursor)def close(self):self.cursor.close()self.db.close()def get_one(self, sql):res = Nonetry:self.connect()self.cursor.execute(sql)res = self.cursor.fetchone()self.close()except Exception:print("查询失败")return resdef get_all(self, sql):res = ()try:self.connect()self.cursor.execute(sql)res = self.cursor.fetchall()self.close()except Exception:print("查询失败")return res## def get_all_obj(self, sql, tableName, *args):#     resList = []#     filedsList = []#     if (len(args) > 0):#         for item in args:#             filedsList.append(item)#     else:#         filedsSql = "select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s' and table_schema = '%s'"%(#             tableName,#             self.dbName#         )#         fields = self.get_all(filedsSql)#         for item in fields:#             filedsList.append(item[0])##     res = self.get_all(sql)#     for item in res:#         obj = {}#         count = 0#         for x in item:#             obj[filedsList[count]] = x#             count += 1#         resList.append(obj)#     return resListdef insert(self, sql):return self.__edit(sql)def update(self, sql):return self.__edit(sql)def delete(self, sql):return self.__edit(sql)def __edit(self, sql):count = 0try:self.connect()self.cursor.execute(sql)self.db.commit()count = self.cursor.lastrowidself.close()except Exception:print("事务提交失败")self.db.rollback()return count

orm.py(只有增加功能)

from application import Applicationclass ORM(Application):def save(self):tableName = (self.__class__.__name__).lower()fieldsStr = valuesStr = "("for field in self.__dict__:  # {'name': 1, 'gender': 2}fieldsStr += (field + ",")if isinstance(self.__dict__[field], str):valuesStr += ("'" + self.__dict__[field] + "',")else:valuesStr += (str(self.__dict__[field]) + ",")fieldsStr = fieldsStr[:len(fieldsStr) - 1] + ")"valuesStr = valuesStr[:len(valuesStr) - 1] + ")"sql = "insert into " + tableName + " " + fieldsStr + " values" + valuesStrself.db.insert(sql)def delete(self):passdef update(self):pass@classmethoddef all(self):print("all")def filter(self):pass

models.py

from db.orm import ORMclass Student(ORM):def __init__(self, sname, gender):self.name = snameself.gender = gender

server.py

from tornado import ioloop  # tornado的核心IO循环模块,封装了linux的epoll和BSD的kqueue
from tornado import httpserver
import application
import configif __name__ == '__main__':app = application.Application()http_server = httpserver.HTTPServer(app)http_server.bind(config.options.get("port"))  # 第三种方式http_server.start(1)  # 第三种方式ioloop.IOLoop.current().start()

config.py

import osBASE_DIRS = os.path.dirname(__file__)# 参数
options = {"port": 8000
}# 数据库配置
mysql = {"host": "127.0.0.1","user": "root","passwd": "","dbName": "db1"
}# 配置settings = {"static_path": os.path.join(BASE_DIRS, "static"),"template_path": os.path.join(BASE_DIRS, "templates"),# """# 加密cookie# """"cookie_secret": "G0SAhaKwQ6eU/lqXoW+b3g/at/cy1EKDluw/JcFlYtA=",# """# 设置tornado是否在调试模式下# 为Ture时:#     1.在Ture下可以自动重启(有改动时)#     2.取消缓存编译的模板(页面缓存)#     3.取消缓存静态文件的hash值(CSS样式改动的缓存)#     4.提供追踪信息()# """"debug": False,# """# 为Ture时:#     仅仅使用自动重启# """"autoreload": True,# """# 为Fale:#     仅仅取消缓存编译的模板# """"compiled_template_cache": False,# """# 为Fale:#     仅仅取消缓存静态文件的hash值# """"static_hash_cache": False,# """# 为True:#     仅仅提供追踪信息# """"serve_traceback": True,"xsrf_cookie": True,"login_url": "/login"
}

application.py

from db.mysql_db import LongMySQl
from tornado import web
from views import index
import config
import osclass Application(web.Application):def __init__(self):handlers = [# (r"/", index.IndexHandler),(r"/getone", index.GetOne, {"first": "1", "second": "2"}),  # 传参web.url(r"/parameter", index.ParameterHandler, name="parameter"),  # 反向解析(r"/ret_json", index.JsonHandler),  # json(r"/ret_json2", index.Json2Handler),  # json2(r"/header", index.HeaderHandler),  # 响应头(r"/status", index.StatusHandler),  # 状态码(r"/redirect", index.RedirectHandler),  # 重定向(r"/errostatus", index.ErrorHandler),  # 抛出错误(r"/re/(\w+)", index.ReHandler),  # 正则匹配# (r"/re/(?P<p1>\w+)", index.ReHandler),  # 正则匹配(r"/reget", index.ReGet),  # get请求获取参数(r"/studentdb", index.StudentsHandler),  # 数据库操作(r"/cookie", index.CookieHandler),  # cookie(r"/sccookie", index.ScCookieHandler),  # 加密cookie(r"/cookienum", index.CookieCountHandler),  # cookie记录访问次数(r"/xsrf", index.XsrfHandler),  # xsrf(r"/setxsrf", index.SetXsrfHandler),  # xsrf(r"/login", index.LoginHandler),  # xsrf(r"/auth", index.AuthHandler),  # auth认证# (r"/callbackasy", index.CallbackAsy),  # 回调函数异步请求(r"/genasy", index.GenvenletAsy),  # 回调函数异步请求(r"/genasy2", index.GenvenletAsy2),  # 回调函数异步请求(r"/chat", index.ChatHandler),  # websocket(r"/home", index.HomeHandler),  # 聊天home页面(r"/(.*)$", index.StaticFileHandler,{"path": os.path.join(config.BASE_DIRS, "static/html"), "default_filename": "main.html"}),  # 主页]super(Application, self).__init__(handlers, **config.settings)  # 执行web.Application的__init__方法self.db = LongMySQl(config.mysql.get("host"),config.mysql.get("user"),config.mysql.get("passwd"),config.mysql.get("dbName"),)

index.py(*****)

from tornado.httpclient import AsyncHTTPClient
from tornado.web import RequestHandler
# from tornado.httputil import HTTPFile
from tornado.websocket import WebSocketHandler
from tornado import web
import config
import tornado
import json
import os"""
视图:request对象:self.request.XXXrequest.methodrequest.postrequest.urirequest.hostrequest.pathrequest.query  # 请求参数部分request.version  # HTTP版本request.headers  # 字典类型request.body  # 请求体数据request.remote_ip  # 客户端iprequest.files  # 用户上传的文件响应:self.write("xxxx")   # 可以写多个self.finish()  # 在finish下边就不要再写write请求方式:get, post, delete, put, patch, head(类似于get请求,只不过响应中没有具体的内容,用户获取报头),options(返回URL支持的所有HTTP方法) 请求函数:initialize()prepare()  # 在请求方法之前执行 类似于中间件set_default_headers()write_error()on_finish()  # 在请求处理后执行,进行资源清理释放,或者日志处理执行顺序:在正常情况下:set_default_headers,initialize,prepare,(HTTP方法),on_finish在抛出错误时:set_default_headers,initialize,prepare,(HTTP方法),set_default_headers,write_error,on_finish"""
"""
模板:1.配置模板路径2.渲染并返回:self.render(),self.render("index.html", num=100, dit=dict, **dict),3.语法:{{var}},{{a+b}}(可以是表达式){{dit["name"]}}  #  字典取值4.条件语句:{% if xxx %}{% elif xxx %}{% else %}{% end %}5.循环:{" for i in arr "}{" end "}6.函数:static_url():<link rel="stylesheet" href="{{static_url('css/my_css.css')}}">优点: 创建了一个基于文件内容的hash值,并将其添加到URL末尾,这个hash值总能保证加载的总是最新版本自定义函数:def sum(a, b):return a + b                self.render(),self.render("index.html", sum=sum){{sum(10,20)}}7.转义raw:str = "<h1>nihao<h1\>"  {% raw str %}autoescape:{% autoescape None %}{{ str }}在配置中修改:setting中添加:"autoescape": None{{escape(str)}}开启转义8.继承:{% extends "base.html" %}{% block main %}{% end %}d9.静态文件static_pathStaticFileHandler: 是tornado预制的用来提供静态资源的handler可以通过tornado.web.StaticFileHandler(r"/(.*)$", StaticFileHandler, {"path": os.path.join(config.BASE_DIRS, "static/html"),"default_filename": "index.html"}),  # 指定访问静态文件路径,和默认文件(写在所有路由下面)10.数据库:tornado未带ORM安全cookie:setting中设置:"cookie_secret" = "xxxxxxxxxxx"   # 用base64与uuid生成ret = base64.b64encode(uuid.uuid4().bytes+uuid.uuid4().bytes)异步:from tornado.httpclient import AsyncHTTPClientfrom tornado.httpclient import HTTPResponse, HTTPRequest因为epoll主要用来解决网络IO的并发问题,所以Tornado的异步也是主要体现在网络的IO异步上,即异步web请求AsyncHTTPClient: 提供异步web请求客户端,用来进行异步web请求fetch(request, callback = None)  用于执行一个web请求,并异步响应返回一个HTTPResponserequest可以是一个url, 也可以是一个HTTPRequest对象
"""class IndexHandler(RequestHandler):"""处理get请求"""def get(self, *args, **kwargs):url = self.reverse_url("parameter")self.write("hello world  <a href='{}'>到反向解析界面</a>".format(url))  # 做出响应数据class GetOne(RequestHandler):"""接收参数,写死的参数"""def initialize(self, first, second):"""接收参数(在执行get之前执行):return:"""self.first = firstself.second = seconddef get(self, *args, **kwargs):print(self.first, self.second)self.write("hello world")class ParameterHandler(RequestHandler):"""获取用户传参,反向解析url"""def get(self, *args, **kwargs):page = self.get_query_argument("page")  # 获取用户传参self.write("我是反向解析url过来的")class JsonHandler(RequestHandler):"""返回json数据"""def get(self, *args, **kwargs):per = {"code": 1001,"msg": ""}per_json = json.dumps(per)self.set_header("Content-Type", "application/json;charset=UTF-8")  # 设置响应头"""返回的Content-Type为text/html"""self.write(per_json)class Json2Handler(RequestHandler):"""返回json数据"""def get(self, *args, **kwargs):per = {"code": 1001,"msg": ""}"""write方法可以帮你转成json字符串,返回的Content-Type类型为application/json类型"""self.write(per)class HeaderHandler(RequestHandler):"""设置响应头"""def set_default_headers(self):"""在get之前调用:return:"""self.set_header("Content-Type", "text/html;charset=UTF-8")class StatusHandler(RequestHandler):"""设置状态码"""def get(self, *args, **kwargs):"""若reason值为None,则状态码为正常值(如404,500....)"""self.set_status(status_code=1000, reason="描述状态码")self.write("xxxxxxx")class RedirectHandler(RequestHandler):"""重定向"""def get(self, *args, **kwargs):self.redirect("/")class ErrorHandler(RequestHandler):"""抛出错误信息"""def write_error(self, status_code, **kwargs):code = 200if status_code == 500:code = 500self.write("服务器内部出错")  # 返回500界面elif status_code == 404:code = 400self.write("资源不存在")  # 返回404界面self.set_status(code)def get(self, *args, **kwargs):flag = int(self.get_query_argument("flag"))if flag == 0:self.send_error(500)self.write("你是对的")class ReHandler(RequestHandler):def get(self, p1, *args, **kwargs):print(p1)self.write("re...")class ReGet(RequestHandler):"""获取请求数据:无论get请求还是post请求都可以用:self.get_argument()sself.get_argument()"""def get(self, *args, **kwargs):"""获取get请求数据self.get_query_argument(name="", default=ARG_DEFAULT, strip=True)如果出现同名参数,则返回最后一个值的结果;default:若没有name则返回默认值若default也未设置,则报错strip:去除左右空格"""# page_list = self.get_query_arguments()  # 这样拿到的是一个传参列表# page = self.get_query_argument("page")  # 拿到单个数据self.render("login.html")def post(self, *args, **kwargs):"""获取post请求数据self.get_body_argument(name="", default=ARG_DEFAULT, strip=True):param args::param kwargs::return:"""username = self.get_body_argument("username", strip=True)password = self.get_body_argument("password", strip=True)"""file数据格式:{'img': [{'filename': 'mmexport1526622518750.jpg', 'body':b"xxxxxx", 'content_type': 'image/jpeg'}{'filename': 'mmexport1526622518750.jpg', 'body':b"xxxxxx", 'content_type': 'image/jpeg'}]"file":[{'filename': 'mmexport1526622518750.jpg', 'body':b"xxxxxx", 'content_type': 'image/jpeg'}]}"""files = self.request.filesfor file in files:filearr = files[file]for fileobj in filearr:# 存储file_path = os.path.join(config.BASE_DIRS, "upload/" + fileobj.filename)with open(file_path, "wb") as f:f.write(fileobj.body)hobby = self.get_body_arguments("hobby", strip=True)print(username, password, hobby, files)  # 李龙飞 123 ['pain', 'read', 'run']self.write("okk")

login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body>
<form action="/reget" method="post" enctype="multipart/form-data">姓名:<input type="text" name="username"><hr>密码:<input type="password" name="password"><hr>文件:<input type="file" name="file"><hr>爱好:<input type="checkbox" value="pain" name="hobby">画画<input type="checkbox" value="read" name="hobby">读书<input type="checkbox" value="run" name="hobby">跑步<input type="submit" name="登录"><span>第{{count}}次登录</span>
</form>
</body>
</html>

class StudentsHandler(RequestHandler):"""数据库操作"""def get(self, *args, **kwargs):sql = "select sname, gender from student"student_list = self.application.db.get_all(sql)print(student_list)self.render("student_show.html", student_list=student_list)

student_show.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>学生列表</title>
</head>
<body>
<ul><li style="color: red">姓名--------性别</li>{% for student in student_list %}<li>{{ student["sname"] }} ------->{{ student["gender"] }}</li>{% end %}
</ul>
</body>
</html>

class CookieHandler(RequestHandler):"""self.set_cookie(name="",  # cookie名value="",  # cookie值domain="",  # 提交cookie时匹配的域名expires=None,  # 设置有效期,可以是时间戳,时间元组,datetime型, 为UTC时间path="/",  # 提交cookie时默认的路径expires_days=5  # 设置有效期天数, 优先级低于expires)执行清除cookie后,并不是立即删除历览器的cookie,而是给cookie值设置为空,并改变其有效期限为失效,真正删除cookies是由浏览器自己去清理的"""def get(self, *args, **kwargs):self.set_cookie("li", "youxiu")  # 实质上是调用了self.set_header("Set-Cookie", "li=youxiu;path=/")# self.get_cookie("li", "未登录")  # 获取cookie# self.clear_cookie("li")  # 清除cookie# self.clear_all_cookies(path="/", domain=None)  # 删除同时匹配path和domain的所有cookieself.write("cookie")class ScCookieHandler(RequestHandler):"""uuid, base64secret = base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)在settings中设置 "cookie_secret": "G0SAhaKwQ6eU/lqXoW+b3g/at/cy1EKDluw/JcFlYtA=",self.set_secure_cookie(name="", value="", expires_days=30, version=None, **kwargs)self.get_secure_cookie(name="", value="", max_age_days=31, min_version=None)  #  max_age_days:过滤出在这个期限内的时间"""def get(self, *args, **kwargs):self.set_secure_cookie("key", "123")  # 有问题, 设置不上self.write("...")class CookieCountHandler(RequestHandler):"""cookie计数"""def get(self, *args, **kwargs):count = self.get_cookie("count", default=None)if count:count = int(count)count += 1else:count = 1self.set_cookie("count", str(count), expires=1)self.write("第{}次访问".format(str(count)))class XsrfHandler(RequestHandler):"""setting中设置:"xsrf_cookie": True模板中添加{% module xsrf_form_html() %}也可以发ajax请求:$.ajax({.....headers:{"X-XSRFToken": "xsrf_token"}})"""def get(self, *args, **kwargs):count = self.get_cookie("count", default=None)if not count:count = 1self.render("postfilexsrf.html", count=count)def post(self, *args, **kwargs):count = self.get_cookie("count", default=None)if count:count = int(count)count += 1else:count = 1self.set_cookie("count", str(count))self.redirect("/xsrf")

postfilexsrf.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body>
<form action="/xsrf" method="post" enctype="multipart/form-data">姓名:<input type="text" name="username"><hr>密码:<input type="password" name="password"><hr>文件:<input type="file" name="file"><hr>爱好:<input type="checkbox" value="pain" name="hobby">画画<input type="checkbox" value="read" name="hobby">读书<input type="checkbox" value="run" name="hobby">跑步<input type="submit" name="登录"><span>第{{count}}次登录</span>
</form><script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>function getCookie(name){var $cookie = document.cookie.match("\\b" + name + "=([^;]*)\\b")return $cookie ? $cookie[1]:undefined}$.ajax({url: "",method: "POST",data: {},success:function (data) {},header: {"X_XSRFToken": getCookie("_xsrf")}})
</script>
</body>
</html>

class SetXsrfHandler(RequestHandler):def get(self, *args, **kwargs):self.xsrf_tokenself.finish()class StaticFileHandler(web.StaticFileHandler):"""在主页就设置"_xsrf""""def __init__(self, *args, **kwargs):super(StaticFileHandler, self).__init__(*args, **kwargs)self.xsrf_tokenclass LoginHandler(RequestHandler):"""登录界面"""def get(self, *args, **kwargs):next = self.get_argument("next", "/")url = "login?next=" + nextself.render("login2.html", url=url)def post(self, *args, **kwargs):next = self.get_argument("next", "/")name = self.get_body_argument("username")passwd = self.get_body_argument("password")print(name, passwd)if name == "long" and passwd == "123":print(next)self.redirect(next + "?flag=logined")else:self.redirect("/login?next=" + next)

login2.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>登录</title>
</head>
<body>
<form action="{{ url }}" method="post" enctype="multipart/form-data">{% module xsrf_form_html() %}姓名:<input type="text" name="username"><hr>密码:<input type="password" name="password"><hr>文件:<input type="file" name="file"><hr>爱好:<input type="checkbox" value="pain" name="hobby">画画<input type="checkbox" value="read" name="hobby">读书<input type="checkbox" value="run" name="hobby">跑步<input type="submit" name="登录"><span></span>
</form>
</body>
</html>

class AuthHandler(RequestHandler):"""认证"""def get_current_user(self):"""若返回为Ture则验证成功若返回值为False则重定向到settings 中login_url所指定的路由:return:"""flag = self.get_argument("flag", default="")return flag@web.authenticated  # 认证装饰器def get(self, *args, **kwargs):self.write("你好啊")# class CallbackAsy(RequestHandler):
#     """
#     用回调函数实现异步请求(这个有问题)
#     """
#     def on_response(self, response):
#         if response.error:
#             self.send_error(500)
#         else:
#             data = response.body
#             self.write(data)
#         self.finish()
#
#     @web.asynchronous
#     def get(self, *args, **kwargs):
#         url = "www.baidu.com"
#         client = AsyncHTTPClient()  # 创建客户端
#         client.fetch(url, self.on_response)class GenvenletAsy(RequestHandler):"""用协程实现异步请求"""@web.gen.coroutinedef get(self, *args, **kwargs):url = "https://www.baidu.com"client = AsyncHTTPClient()  # 创建客户端res = yield client.fetch(url)if res.error:self.send_error(500)else:data = res.bodyself.write(data)class GenvenletAsy2(RequestHandler):def get(self, *args, **kwargs):res = yield self.getData()self.write(res)@web.gen.coroutinedef getData(self):url = "www.baidu.com"client = AsyncHTTPClient()data = yield client.fetch(url)if data.error:data = {"ret": 0}else:data = data.bodyraise web.gen.Return(data)class HomeHandler(RequestHandler):def get(self, *args, **kwargs):self.render("home.html")class ChatHandler(WebSocketHandler):user = []  # 存储每一个人的信息def open(self):"""websocket连接建立后执行:return:"""self.user.append(self)for user in self.user:user.write_message("[{}]登录了".format(self.request.remote_ip))def on_message(self, message):"""当客户端发送消息过来时调用:param message::return:"""for user in self.user:user.write_message("[{}]说:{}".format(self.request.remote_ip, message))#def on_close(self):"""当websocket链接关闭后调用:return:"""self.user.remove(self)for user in self.user:user.write_message("[{}]退出了".format(self.request.remote_ip))## def write_message(self, message, binary=False):#     """#     主动向客户端发送消息#     :param message: 可以是字符串,或者字典(转成json字符串),#     :param binary:如果binary为false,则message会以UTF-8编码发送,为Ture发送二进制,字节码#     :return:#     """#     passdef close(self, code=1001, reason="123"):"""关闭websocket链接, 服务器主动关闭:param code::param reason::return:"""passdef check_origin(self, origin):"""判断源origin, 对于符合的请求允许链接,同源策略:param origin::return:"""return True

home.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>聊天界面</title>
</head>
<body>
<div id="contents" style="width: 500px;height: 500px;overflow: auto"><div><input type="text" id="message"><button id="send">发送</button>
</div>
</div>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>var ws = new WebSocket("ws://127.0.0.1:8000/chat"); // 建立连接ws.onmessage = function(e){$("#contents").append("<p>" + e.data + "</p>")};  // 接收服务器消息$("#send").on("click", function () {var $mes = $("#message");var message = $mes.val();ws.send(message);$mes.val("")})  // 向服务器发消息
</script></body>
</html>

python笔记(tornado初识)相关推荐

  1. 轩小陌的Python笔记-day17 初识面向对象

    第三模块 面向对象&网络编程&并发编程 第三模块包含的知识内容: 面向对象,Python中支持两种编程方式来编写代码,分别是:函数式编程.面向对象式编程. 函数式 # 定义函数,在函数 ...

  2. C语言学习笔记-P1 初识C语言(1)

    C语言学习笔记-P1 初识C语言(1) P1 初识C语言(1) 一.什么是C语言 1.定义 2.发展 二.第一个C语言程序 Hello World 三.数据类型 四.变量,常量 未完待续!!! P1 ...

  3. python中tornado的第一个例子

    python中tornado的第一个例子 1  先安装tornado pip install tornado 2 新建tor.py 记住不能建立 tornado.py 这样的名字  不然会报错 Imp ...

  4. tkinter 笔记: radiobutton 选择按钮(莫烦python笔记)

    1 主体框架还是那个主体框架 window = tk.Tk() window.title('my window') window.geometry('500x500') 2 设置tkinter的文字变 ...

  5. tkinter 笔记:列表部件 listbox (莫烦python 笔记)

    1  主体框架 主体框架部分还是 import tkinter as tkwindow = tk.Tk() #创建窗口window.title('my window') #窗口标题window.geo ...

  6. python笔记: 生成器

    元素按照某种算法推算出来,我们在循环的过程中不断推算出后续的元素 不必创建完整的list,从而节省了大量的空间 这种一边循环一遍计算的机制,称之为生成器generator 1 列表生成器 把列表生成式 ...

  7. Hadoop学习笔记—4.初识MapReduce

    一.神马是高大上的MapReduce MapReduce是Google的一项重要技术,它首先是一个 编程模型 ,用以进行大数据量的计算.对于大 数据量的计算,通常采用的处理手法就是并行计算.但对许多开 ...

  8. Python下tornado实现webSocket实现

    在上一篇中我们简单介绍了WebSocket及Python下Tornado架构中Websocket的实现,下面我们就直接上一个源码: 服务器端程序: -------------------------- ...

  9. python输出字体的大小_Toby的Python笔记 | 预备知识:安装openpyxl学做电子表格

    Toby的Python笔记 | 预备知识:安装openpyxl学做电子表格 Python 需要创建和读取excel表里面的数据,需要用 openpyxl 这个包,今天安装好备用. 首先,进入C命令窗口 ...

最新文章

  1. JAVA中文字符串编码--GBK转UTF-8
  2. 腾讯企点总经理张晔:To B企业存在的价值是什么?丨鲸犀峰会
  3. AcWing算法提高课 Level-3 第二章 搜索
  4. wxWidgets:创建应用程序的 DLL
  5. winrar5.50去广告教程(仅供学习使用)
  6. android 使用adb远程调试
  7. 从numpy里加载_PyTorch强化:01.PyTorch 数据加载和处理
  8. 代码描述10911 - Forming Quiz Teams
  9. 网络专业人士笔记(7~11章)
  10. EasyCVR接入Ehome协议设备PS流解析失败?一文分析PS流解析注意点
  11. arma找不到合适的模型_ARMA模型建模过程中存在的问题分析.htm
  12. 全志A40I tina系统蓝牙wifi调试方法
  13. 电阻触摸屏 linux 校准软件,android 电阻单点触摸屏校准
  14. com词根词缀_词根词缀记忆大全---经典详细的总结
  15. 闭环系统的零极点图判定稳定性_零极点与系统稳定关系 拉氏变换的收敛域...
  16. 在标准IO库中,rewind函数作用?
  17. polyfit及poly1d多项式拟合
  18. 房产抵押贷款利率划算吗
  19. 使用LSTM完成简单的中英翻译
  20. 如何恢复移动硬盘数据删除的文件

热门文章

  1. LeetCode 518 和LeetCode 377 的比较
  2. 位图php,ps中什么是位图
  3. 嵌入式Linux教程—裸机、应用、驱动完整教程目录
  4. Android APK签名 JKS 密钥库使用专用格式。建议使用 “keytool -importkeystore -srckeystore E:\xxxxxx- pkcs12“ 迁移到行业标准格式
  5. Kubernetes 单节点安装Clickhouse
  6. PageHelper的使用
  7. TiDB 在摩拜单车的深度实践及应用 1
  8. 惠普激光打印机硒鼓加碳粉图解篇
  9. 问渠哪得清如许,为有源头活水来之TCP / UDP
  10. vue下载二进制流文件转为Excel文件