python笔记(tornado初识)
一、tornado介绍
轻量级web框架
异步非阻塞IO处理方式
出色的抗负载能力
优异的处理性能,不依赖多进程/多线程,一定程度上解决GIL问题
WSGI全栈替代产品,推荐同时使用其web框架和HTTP服务器
socket连接建立过程
WSGI把应用(Application)和服务器(Server)结合起来,Tornado既可以是WSGI应用也可以是WSGI服务。
既是WebServer也是WebFramework.
tornado生命周期
二、初步简单使用
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()
多进程形式
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()
常用形式
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()
带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
协程实现异步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异步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初识)相关推荐
- 轩小陌的Python笔记-day17 初识面向对象
第三模块 面向对象&网络编程&并发编程 第三模块包含的知识内容: 面向对象,Python中支持两种编程方式来编写代码,分别是:函数式编程.面向对象式编程. 函数式 # 定义函数,在函数 ...
- C语言学习笔记-P1 初识C语言(1)
C语言学习笔记-P1 初识C语言(1) P1 初识C语言(1) 一.什么是C语言 1.定义 2.发展 二.第一个C语言程序 Hello World 三.数据类型 四.变量,常量 未完待续!!! P1 ...
- python中tornado的第一个例子
python中tornado的第一个例子 1 先安装tornado pip install tornado 2 新建tor.py 记住不能建立 tornado.py 这样的名字 不然会报错 Imp ...
- tkinter 笔记: radiobutton 选择按钮(莫烦python笔记)
1 主体框架还是那个主体框架 window = tk.Tk() window.title('my window') window.geometry('500x500') 2 设置tkinter的文字变 ...
- tkinter 笔记:列表部件 listbox (莫烦python 笔记)
1 主体框架 主体框架部分还是 import tkinter as tkwindow = tk.Tk() #创建窗口window.title('my window') #窗口标题window.geo ...
- python笔记: 生成器
元素按照某种算法推算出来,我们在循环的过程中不断推算出后续的元素 不必创建完整的list,从而节省了大量的空间 这种一边循环一遍计算的机制,称之为生成器generator 1 列表生成器 把列表生成式 ...
- Hadoop学习笔记—4.初识MapReduce
一.神马是高大上的MapReduce MapReduce是Google的一项重要技术,它首先是一个 编程模型 ,用以进行大数据量的计算.对于大 数据量的计算,通常采用的处理手法就是并行计算.但对许多开 ...
- Python下tornado实现webSocket实现
在上一篇中我们简单介绍了WebSocket及Python下Tornado架构中Websocket的实现,下面我们就直接上一个源码: 服务器端程序: -------------------------- ...
- python输出字体的大小_Toby的Python笔记 | 预备知识:安装openpyxl学做电子表格
Toby的Python笔记 | 预备知识:安装openpyxl学做电子表格 Python 需要创建和读取excel表里面的数据,需要用 openpyxl 这个包,今天安装好备用. 首先,进入C命令窗口 ...
最新文章
- JAVA中文字符串编码--GBK转UTF-8
- 腾讯企点总经理张晔:To B企业存在的价值是什么?丨鲸犀峰会
- AcWing算法提高课 Level-3 第二章 搜索
- wxWidgets:创建应用程序的 DLL
- winrar5.50去广告教程(仅供学习使用)
- android 使用adb远程调试
- 从numpy里加载_PyTorch强化:01.PyTorch 数据加载和处理
- 代码描述10911 - Forming Quiz Teams
- 网络专业人士笔记(7~11章)
- EasyCVR接入Ehome协议设备PS流解析失败?一文分析PS流解析注意点
- arma找不到合适的模型_ARMA模型建模过程中存在的问题分析.htm
- 全志A40I tina系统蓝牙wifi调试方法
- 电阻触摸屏 linux 校准软件,android 电阻单点触摸屏校准
- com词根词缀_词根词缀记忆大全---经典详细的总结
- 闭环系统的零极点图判定稳定性_零极点与系统稳定关系 拉氏变换的收敛域...
- 在标准IO库中,rewind函数作用?
- polyfit及poly1d多项式拟合
- 房产抵押贷款利率划算吗
- 使用LSTM完成简单的中英翻译
- 如何恢复移动硬盘数据删除的文件
热门文章
- LeetCode 518 和LeetCode 377 的比较
- 位图php,ps中什么是位图
- 嵌入式Linux教程—裸机、应用、驱动完整教程目录
- Android APK签名 JKS 密钥库使用专用格式。建议使用 “keytool -importkeystore -srckeystore E:\xxxxxx- pkcs12“ 迁移到行业标准格式
- Kubernetes 单节点安装Clickhouse
- PageHelper的使用
- TiDB 在摩拜单车的深度实践及应用 1
- 惠普激光打印机硒鼓加碳粉图解篇
- 问渠哪得清如许,为有源头活水来之TCP / UDP
- vue下载二进制流文件转为Excel文件