Flask:Flask-Session + WTForms + 数据库连接池 + websocket工作原理

Flask-Session组件

1、首先要安装Flask-Session组件

  方式一:

    利用pycharm中settings中安装该组件

  

  方式二:

pip3 install Flask-Session

2、简单的使用

from flask import Flask,session
from flask_session import Session
from redis import Redis

app = Flask(__name__)
# app.secret_key = "asdf%^&*"   # 使用组件就不用sercret_key了,但是需要在app对象进行一下配置

app.config["SESSION_TYPE"] = "redis"    # 必须是小写redis
app.config["SESSION_REDIS"] = Redis(host="127.0.0.1",port=6379,db=6)    # 主机 端口 数据库

Session(app)   # 将app对象添加到Session中

@app.route("/")
def index():
    session["user"] = "value"     #  以后通过get("session:a40f1324-7847-46aa-bfa7-ae39f3dc64cd") Laura获取user value
    return "hello"

if __name__ == '__main__':
    app.run(debug=True)

3、看源码

  Flask中的session 需要执行 session_interface - open_session

 

WTForms组件

1、安装 WTForms

   在pycharm中搜索 WTForms 中然后进行安装

  或者 pip3 insatall WTForms

2、基本的使用

  类似于 django 中的 form 组件,编写 wtforms 类可以自动生成 form 表单中的 input 标签

  xx.py

  重点要好好看看

from flask import Flask, render_template, request
from wtforms import Form
from wtforms import validators
from wtforms.fields import simple, core  # 所有的字段类型都是这两个里面拿的

app = Flask(__name__)


class LoginForm(Form):    # 继承Form
    username = simple.StringField(
        label="用户名",  # 标签标记
        validators=[validators.DataRequired(message="用户名不能为空"),
                    validators.Length(min=3, max=8, message="用户名长度不再范围之内")],  # 校验条件 可迭代条件
        # description='11111111111',  # 描述标记
        id="user_id",  # 标签ID,不指明id 默认是字段名 username
        default=None,  # 默认值
        widget=None,  # 默认组件(input type="text") 在StringField中已经被实例化了
        render_kw={"class": "my_login"},  # {"class":"my_login"}     添加类名
    )
    password = simple.PasswordField(
        label="密码",  # 标签标记
        validators=[validators.DataRequired(message="密码不能为空"),
                    validators.Length(min=3, max=16, message="密码长度不再范围之内"),
                    validators.Email(message="密码必须符合邮箱规则")],  # 不知道
        # description='11111111111',  # 描述标记
        id="user_id",  # 标签ID
        default=None,  # 默认值
        widget=None,  # 默认组件(input type="password") 在StringField中已经被实例化了
        render_kw={"class": "my_password"},  # {"class":"my_password"}
    )


class RegForm(Form):
    username = simple.StringField(
        label="用户名",  # 标签标记
        validators=[validators.DataRequired(message="用户名不能为空"),
                    validators.Length(min=3, max=8, message="用户名不是长了就是短了")],  # 校验条件 可迭代条件
    )

    password = simple.PasswordField(
        label="密码",  # 标签标记
        validators=[validators.DataRequired(message="密码不能为空"),
                    validators.Length(min=3, max=16, message="密码不是长了就是短了"),
                    validators.Email(message="密码必须符合邮箱规则")],
    )

    repassword = simple.PasswordField(
        label="确认密码",  # 标签标记
        validators=[validators.EqualTo(fieldname="password", message="眼神未确认")]
    )

    gender = core.RadioField(
        label="性别",
        coerce=str,
        choices=(
            ("1", ""),
            ("2", "")
        ),
        default="1"
    )

    hobby = core.SelectMultipleField(
        label="爱好",
        validators=[validators.Length(min=2, max=4, message="癖好有问题")],
        coerce=int,
        choices=(
            (1, "fengjie"),
            (2, "luoyufeng"),
            (3, "lixueqin"),
            (4, "wuyifan"),
            (5, "panta")
        ),
        default=(1, 3, 5)
    )


@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "GET":
        fm = LoginForm()
        return render_template("index.html", wtf=fm)
    else:
        fm = LoginForm(request.form)
        if fm.validate():
        # 验证果果之后,可以做一些逻辑处理
return fm.data.get("password") else: return render_template("index.html", wtf=fm) @app.route("/reg", methods=["GET", "POST"]) def reg(): if request.method == "GET": rf = RegForm() return render_template("reg.html", rf=rf) else: rf = RegForm(request.form) if rf.validate(): print(type(rf.data.get("gender")), rf.data.get("gender")) return rf.data.get("password") else: return render_template("reg.html", rf=rf) if __name__ == '__main__': app.run(debug=True)

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<form action="" method="post" novalidate>
  {{ wtf.username.label }}
  {{ wtf.username }}
  <p><h1>{{wtf.username.errors.0}}</h1></p>
  <p>
      {{ wtf.password.label }}
  {{ wtf.password }}
  </p>
  <p><h1>{{wtf.password.errors.0}}</h1></p>
  <input type="submit" value="登录">
</form>
</body>
</html>

reg.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<form action="" method="post" novalidate>
  {% for field in rf %}
      <!-- 对实例化对象进行循环后,展示其信息 -->
    <p>{{ field.label }}{{ field }}{{ field.errors.0 }}</p>
  {% endfor %}
  <input type="submit" value="注册">
</form>
</body>
</html>

数据连接池

一、pymysql 回顾

基本用法

import pymysql

mysql_conn = pymysql.connect(host="127.0.0.1",
                             port=3306,
                             user="root",
                             password="",
                             charset="utf8",
                             db="day115")  # 数据库建立连接

c = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)

sql = "select * from users WHERE name='jwb' and age=73 "

res = c.execute(sql)  # 返回满足sql条件的个数,
print(res)
print(c.fetchall())  # 获取所有的,必须 execute 才能 fetchall
print(c.fetchone())  # 获取一个

c.close()
mysql_conn.close()

 

二、数据连接池

1、连接池的基本使用

dbpool.py

import pymysql
from DBUtils.PooledDB import PooledDB
# 建立数据库的连接池,实例化设置一些参数
POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
    maxshared=3,
    # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服务端,检查是否服务可用。
    #  如:0 = None = never,
    # 1 = default = whenever it is requested,
    # 2 = when a cursor is created,
    # 4 = when a query is executed,
    # 7 = always

    # 下面的这些参数是写入 creator=pymysql 中的 pymysql 的
    host="127.0.0.1",
    port=3306,
    user="root",
    password="",
    charset="utf8",
    db="day115"   # 数据库名为 day115
)


'''
# 建立连接池以后这里是基本使用方法
conn = POOL.connection()           # pymysql - conn
cur = conn.cursor(cursor=pymysql.cursors.DictCursor)
sql = "select * from users WHERE name='jwb' and age=73 "
res = cur.execute(sql)
print(cur.fetchall())
conn.close()
'''

 

自制 sqlhelper.py

  使用上面建立的数据连接池,进行增删改查

import pymysql

from .dbpool import POOL  # dbpool是自己写的模块,导入建立的数据连接池


def create_conn():
    conn = POOL.connection()
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

    return conn, cursor


def close_conn(conn, cursor):
    cursor.close()
    conn.close()


def insert(sql, args):
    conn, cursor = create_conn()
    res = cursor.execute(sql, args)
    conn.commit()
    close_conn(conn, cursor)
    return res


def fetch_one(sql, args):
    conn, cursor = create_conn()
    cursor.execute(sql, args)
    res = cursor.fetchone()
    close_conn(conn, cursor)
    return res


def fetch_all(sql, args):
    conn, cursor = create_conn()
    cursor.execute(sql, args)
    res = cursor.fetchall()
    close_conn(conn, cursor)
    return res


# 插入操作
sql = "insert into users(name,age) VALUES (%s, %s)"
insert(sql, ("mjj", 9))

# 查询操作
sql = "select * from users where name=%s and age=%s"
print(fetch_one(sql, ("mjj", 9)))

 

Python数据库连接池DragonFire详解

 

websocket工作原理

一、ws基本使用的回顾

xx.py  

from flask import Flask, request
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
from geventwebsocket.websocket import WebSocket

app = Flask(__name__)


@app.route("/")
def index():
    return "hello"


@app.route("/ws")
def ws():
    user_socket = request.environ.get("wsgi.websocket")  # type:WebSocket   # 写了type 下面的receive才会有提示
    while 1:
        msg = user_socket.receive()
        print(msg)
        user_socket.send(msg)


if __name__ == '__main__':
    # app.run(debug=True)
    http_serv = WSGIServer(("0.0.0.0", 9527), app, handler_class=WebSocketHandler)
    http_serv.serve_forever()

ws.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

</body>
<script type="application/javascript">
  var ws = new WebSocket("ws://127.0.0.1:9527/ws");    # 去访问 ws 请求

</script>
</html>

二、自己写websocket,如何握手

  1、手动创建完 websocket 之后

  2、浏览器打开的 ws.html ,在控制台 Console,输入 ws.send("hello"),后台接收到,打印出加密之后的一串  字节型

      b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'

  3、然后 利用上述字节 进行解密操作

 

websocket_hello.py   随便起名

import socket, base64, hashlib

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 9527))
sock.listen(5)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
print(data)
"""
GET /ws HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: session=a6f96c20-c59e-4f33-84d9-c664a2f29dfc\r\n
Sec-WebSocket-Key: MAZZU5DPIxWmhk/UWL2+BA==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n\r\n
"""
# 以下动作是有websockethandler完成的
# magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11


def get_headers(data):
    header_dict = {}
    header_str = data.decode("utf8")
    for i in header_str.split("\r\n"):
        if str(i).startswith("Sec-WebSocket-Key"):
            header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()

    return header_dict


headers = get_headers(data)  # 提取请求头信息
#
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
#Sec-WebSocket-Key: MAZZU5DPIxWmhk/UWL2+BA==
value = headers['Sec-WebSocket-Key'] + magic_string
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())

# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
               "Upgrade:websocket\r\n" \
               "Connection: Upgrade\r\n" \
               "Sec-WebSocket-Accept: %s\r\n" \
               "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"
print(ac.decode('utf-8'))
response_str = response_tpl % (ac.decode('utf-8'))   #字符串拼接
# 响应【握手】信息
conn.send(response_str.encode("utf8"))
#
while True:
    msg = conn.recv(8096)
    print(msg)

三、解密

websocket_jiemi.py   随便起名

#b'\x81\x89\xf3\x99\x81-\x15\x05\x01\xcbO\x1be\x97]'
#b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'
#b'\x81\x83H\xc0x\xa6y\xf2K'

hashstr = b'\x81\x85s\x92a\x10\x1b\xf7\r|\x1c'
# b'\x81 \x85s \x92a\x10\x1b\xf7  \r|\x1c' <126
# \x85s = 

'''
# 解密第二个 出师表中的内容大于125
hashstr = b'\x81\xfe\x02\xdc\x8d\xe8-\xb2hm\xa5W5u\xc8:\x16\x0c\x95(kt\x87W\x00b\xc52\x01\x0c\x95\x1fdi\xbeW9A\xcb\x1c\x0f\x07\x91>iS\xa7W)A\xc9\n\x06\x0c\x95;h`\xab]1d\xca)\x07\r\x9a,j~\x9fW1b\xc2\x0e\x01\x0e\x80\x16eG\xb7W\x00Y\xcb2(\r\x80*iR\x8cV4c\xca\x15\x06\x0c\x94-nh\xafU\t^\xc9\x0c\x00\r\xa0\x19iQ\xa6Z\nK\xc9\n\x00\x0e\xaa:iR\xa3W\x0bm\xc2\x0e\x01\r\x92\x12hW\xbaV4c\xc8\x11&\r\x92*eR\x86V7f\xc8\x16\x1b\x00\xad7bT\xa1U\x16~\xc5\r0\r\xa8:hP\xb0V4c\xcb\x1c\x07\x01\xac5bT\xa1T!Z\xcb8(\x0c\x949iR\xa3[\x14s\xc9\n\x06\x0c\x94-nh\xafZ"r\xc8\x1c\x11\r\x912hT\x8dW\x11K\xc8"!\x07\x91>iS\x88W\x08a\xc87\x05\r\x95/di\xbaW3_\xc2\x0e\x01\x0e\xac\x10hT\xb5W2\x7f\xc8\x11&\x0c\x949kX\xb9]1d\xc9\n\x00\r\x83.hN\xa9Z\nB\xc5=?\x00\xbb6bT\xa1W1}\xc8$6\r\x89\x03iQ\xa4]1d\xc9\t(\r\x8c,hW\x8dZ=g\xc9\x0b\x06\x00\x9a\x1diQ\xb2Q\rj\xc8\x1c&\x0c\x95\x1fhR\xb1V5E\xc2\x0e\x01\x0c\x92\x03iP\x97V5h\xc9\x0f\x1e\x07\x91>dq\xb2U0r\xc55*\r\xbd\x14bT\xa1V5e\xc8\x1c\x11\r\x910hx\xa1Q\rj\xc59(\x0e\xb1;iU\xb1W(P\xca8"\x0f\x8a#hg\xa7V5R\xc8\r-\r\xbb6eh\xa8]1d\xc8\x1c\x11\x0c\x96*kt\xa4W\x02P\xc5\x1c7\r\xa8\x04h`\xbcZ8g\xc2\x0e\x01\x0c\x96\x17kp\x80[\x14s\xc9\n\x06\r\x94\x01kp\xa3V4c\xca"\x0b\x07\x91>iP\xa0W#t\xc83\x02\x0f\x8a3bT\xa1V0W\xc84\x08\r\x89$hT\xafT>}\xc9\x0b\x12\x0b\xad0iV\xa0V5E\xce2\x0c\x0c\x93?dk\xa3[\x0eE\xcb&5\x0c\x949nh\xacZ9Q\xca\x17\x03\x0b\xad3ey\x8eW\x08i\xca\x1f\x04\x07\x91>kE\x89U\x17n\xc5;"\r\x83,bT\xa1W2\x7f\xc5+\x1c\r\x92\x12jR\x82]1d\xcb*"\x0c\x96\x17hm\xa5W5u\xca\x1c\r\x0e\xa6&iS\x88[\x0c\x7f\xc4+\x16\x0c\x959nh\xafT\tr\xc9\t(\x0c\x95\x08hF\x86V5E\xc9\x0b\x06\x0c\x979bT\xa1V7c\xcb%-\r\x89\x15hX\xa2]1d\xcb0\x04\x0c\x96\x17hz\x85V4c\xc2\x0e\x01\x0f\xa9\x04hx\xa3T\x1bU\xc5\x13\x01\x07\x91>hW\xa8Z\x0eU\xc5\x11%\x00\x8c\x17dp\xb4T1g\xc2\x0e\x01\x0e\xb1;ka\xadW4W\xca)\x07\x0b\xad0'
print(chushibiao[1],chushibiao[1]&127)
print(chushibiao[2:4],chushibiao[4:8])
'''
# 将第二个字节也就是 \x85s 第9-16位 进行与127进行位运算
payload = hashstr[1] & 127
print(payload)
if payload == 127:
    extend_payload_len = hashstr[2:10]
    mask = hashstr[10:14]
    decoded = hashstr[14:]
# 当位运算结果等于127时,则第3-10个字节为数据长度
# 第11-14字节为mask 解密所需字符串
# 则数据为第15字节至结尾

if payload == 126:
    extend_payload_len = hashstr[2:4]
    mask = hashstr[4:8]
    decoded = hashstr[8:]
# 当位运算结果等于126时,则第3-4个字节为数据长度
# 第5-8字节为mask 解密所需字符串
# 则数据为第9字节至结尾


if payload <= 125:
    extend_payload_len = None
    mask = hashstr[2:6]
    decoded = hashstr[6:]

# 当位运算结果小于等于125时,则这个数字就是数据的长度
# 第3-6字节为mask 解密所需字符串
# 则数据为第7字节至结尾

str_byte = bytearray()
# b'\x81 \x85s \x92a\x10\x1b \xf7\r|\x1c' <126
for i in range(len(decoded)): # 0  \xf7 ^ \x92a 1 \r ^ \x10 \x1c ^ \x1b
    byte = decoded[i] ^ mask[i % 4]
    str_byte.append(byte)

print(str_byte.decode("utf8"))

四、加密

  该加密算法还存在问题,因为加密完之后,是明文的,暂时还没有解决该问题

websocket_jiami.py    随便起名

import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)

if length < 126:
    token += struct.pack("B", length)
elif length == 126:
    token += struct.pack("!BH", 126, length)
else:
    token += struct.pack("!BQ", 127, length)

msg = token + msg_bytes

print(msg)

 

posted @ 2019-01-14 14:40  葡萄想柠檬  Views(470)  Comments(0)    收藏  举报
目录代码