Flask WebSocket学习笔记

WebSocket

简介:WebSocket是一种全新的协议,随着HTML5的不断完善,越来越多的现代浏览器开始全面支持WebSocket技术了,它将TCP的Socket(套接字)应用在了webpage上,从而使通信双方建立起一个保持在活动连接通道。

运行流程:浏览器通过Javascript向服务器发起WebSocket连接的请求,在WebSocket连接建立成功后,客户端和服务器就可以通过TCP连接传输数据。因为WebSocket连接本质上是TCP连接,不需要每次传输都带上重复的头部数据,所以它的数据传输量比轮询和Comet(长连接)技术小很多。

原理:WebSocket协议是借用HTTP协议的1.01 switch protocol(服务器根据客户端的指定,将协议转换成为Upgrade首部所列的协议)来达到协议转换的,从HTTP协议切换成WebSocket通信协议。

具体连接方式:通过在请求头中添加upgrade:websocket及通信秘钥(Sec-WebSocket-Key),使双方握手成功,建立全双工通信。

WebSocket客户端连接报文

Flask WebSocket学习笔记_第1张图片

WebSocket服务端响应报文

Flask WebSocket学习笔记_第2张图片

1. Flask-SocketIO

要对应安装不然会出现版本问题

pipinstallgevent
pipinstalleventlet
pipinstallFlask-SocketIO==4.3.1
pipinstallpython-engineio==3.13.2
pipinstallpython-socketio==4.6.0

1.1 初始化

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
​
if__name__ == '__main__':
    socketio.run(app)

1.2 客户端向服务器发送数据

客户端和服务端使用SocketIO时,消息都被当作事件进行接收。在客户端,Javascript 通过回调函数处理事件;在Flask-SocketIO服务端,每个事件都有对应的事件函数,类似原生Flask中,路由都有对应的视图函数。

前端代码:




    
    Test
    
    


    

Hello World!

后端代码:

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO, emit
fromthreadingimportThread
importtime
​
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
#socketio = SocketIO()
socketio = SocketIO(logger=True, engineio_logger=True)  # 将日志输出到终端
socketio.init_app(app, cors_allowed_origins='*')    # 解决跨域问题
name_space = '/dcenter' # 命名空间必须与前端一致
​
# 初始化连接函数
@socketio.on('connect', namespace=name_space)
defconnected_msg():
    print('client connected.')
​
# 初始化断开函数
@socketio.on('disconnect', namespace=name_space)
defdisconnect_msg():
    print('client disconnected.')
​
# 路由路径
@app.route('/')
defindex():
    returnrender_template('index.html')
​
# 接收客户端消息(事件命名必须与前端一致)
@socketio.on('my event') # 匿名事件
defhandle_my_custom_event(json):
    print('received json: '+str(json))
    
# 接收客户端消息(事件命名必须与前端一致)
@socketio.on('my event', namespace=name_space) # 绑定命名空间
defhandle_my_custom_event(json):
    print('received json: '+str(json))
​
​
if__name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)​

服务器响应数据:

Flask WebSocket学习笔记_第3张图片

1.3 服务器实时接收数据

前端代码:




    
    Test
    
    
{#    #}


    

Hello World!

发送

后端代码:

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO, emit
fromthreadingimportThread
importmultiprocessing
importtime
​
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
# socketio = SocketIO()
socketio = SocketIO(logger=True, engineio_logger=True)  # 将日志输出到终端
socketio.init_app(app, cors_allowed_origins='*')    # 解决跨域问题
name_space = '/dcenter'
​
# 初始化断开函数
@socketio.on('connect', namespace=name_space)
defconnected_msg():
    print('client connected.')
​
# 初始化断开连接函数
@socketio.on('disconnect', namespace=name_space)
defdisconnect_msg():
    print('client disconnected.')
​
@app.route('/')
defindex():
    returnrender_template('index.html')
​
# 匿名函数
@socketio.on('message')
defhandle_message(message):
    print('received message: '+message)
​
​
if__name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

先访问,点击发送:

Flask WebSocket学习笔记_第4张图片

服务器实时接收结果:

Flask WebSocket学习笔记_第5张图片

1.4 服务器向客户端发送数据

在启用广播选项的情况下发送消息时,连接到命名空间的所有客户端都会收到它,包括发送者。当不使用命名空间时,连接到全局命名空间的客户端会收到消息。请注意,不会为广播消息调用回调。

设置参数broadcast=True就会启动广播功能。

前端代码:




    
    Test
    
    


    

Hello World!

后端代码:

fromflaskimportFlask, render_template
fromflask_socketioimportSocketIO, emit
fromthreadingimportThread
importtime
​
​
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'
#socketio = SocketIO()
socketio = SocketIO(logger=True, engineio_logger=True)  # 将日志输出到终端
socketio.init_app(app, cors_allowed_origins='*')        # 解决跨域问题
name_space = '/dcenter'     # 命名空间
​
# 初始化连接函数
@socketio.on('connect', namespace=name_space)
defconnected_msg():
    print('client connected.')
    
# 回调函数
defcb():
    print('vvv==bbb')
    
# 初始化断开连接函数
@socketio.on('disconnect', namespace=name_space)
defdisconnect_msg():
    print('client disconnected.')
​
# 测试函数向客户端发送数据
deftest():
    event_name = 'dcenter'
    foriinrange(11):
        broadcasted_data = {'data': f"test message!,{i}"}
        # 消息广播
        socketio.emit(event_name, broadcasted_data, broadcast=False, namespace=name_space, callback=cb())
        time.sleep(1)
​
@app.route('/')
defindex():
    # 开启多线程进行消息发送
    td = Thread(target=test)
    td.start()
    returnrender_template('index.html')
​
​
if__name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=5000, debug=True)

访问http://127.0.0.1:5000/后,服务器会在11秒内依次向客户端发送数据

Flask WebSocket学习笔记_第6张图片

1.5 总结

1、命名空间事件命名必须前后端保持一致。

2、服务器的信息发送广播只支持线程广播但不支持进程广播。

2. 房间

实际应用场景中,可能需要给用户分组。比如,聊天室,不同用户只能收到他们所在房间的消息。通过join_room() 和 leave_room() 可以实现上述功能:

from flask_socketio import join_room, leave_room
​
@socketio.on('join')
def on_join(data):
    username = data['username']
    room = data['room']
    join_room(room)
    send(username + ' has entered the room.', room=room)
​
@socketio.on('leave')
def on_leave(data):
    username = data['username']
    room = data['room']
    leave_room(room)
    send(username + ' has left the room.', room=room)

send() 和emit() 函数接受room 参数。

所有客户端连接时,会被分配一个房间。默认房间名称为连接的session ID,Flask中通过request.sid获取该ID。客户端能加入所有存在的房间。客户端断开时,所有它加入的房间都会移除它。上下文外的socketio.send() 和 socketio.emit()也可以接收room参数,来给房间中所有客户端广播。

因为所有客户端在加入时,都被指定了一个私人的房间,所以,如果想要发送消息给指定客户端,也可以通过指定消息的room参数为该客户端session ID来实现。

参考文献:https://zhuanlan.zhihu.com/p/376370796

https://blog.csdn.net/weixin_46020624/article/details/123619230?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167513282616800225584650%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167513282616800225584650&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-123619230-null-null.142^v71^wechat,201^v4^add_ask&utm_term=Flask-SocketIO&spm=1018.2226.3001.4187

你可能感兴趣的:(web,html,websocket,学习,flask)