在flask框架下,服務端和客戶端通過websocket的通信方式主要有兩種,一種是原生的websocket通信,通過引入flask-websockets來實現,這個包只是簡單的對websocket協議進行了簡單的封裝;另外一種就是本文主要講的flask-socketio方式。
先談談自己遇到的坑,因為項目需要websocket通信,起初為了前后端通用性選擇了flask-websockets,但是好不容易測試通過可以互傳信息了,結果發現只要啟動通信,flask框架下的其他接口統統用不了,響應超時,后來想到的原因可能是因為websocket通信使用了和flask接口同樣的端口,導致端口阻塞造成的。於是根據網上建議改用flask-socketio。
服務端
首先是安裝,通過命令行完成:
pip install flask-socketio
然后,在你的flask項目中引入,並初始化:
from flask import Flask, render_template from flask_socketio import SocketIO app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app) if __name__ == '__main__': socketio.run(app)
注意:這里的程序啟動方式要更改為socket特有的模式,用socketio.run(app)代替app.run()。
socketio還支持init_app的初始化方式,可以在創建socketio對象時,先不傳入app實例,啟動程序前再調用init_app進行初始化。
客戶端
客戶端可以用網頁前端連接
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script> <script type="text/javascript" charset="utf-8"> var socket = io.connect('http://' + document.domain + ':' + location.port); socket.on('connect', function() { socket.emit('my event', {data: 'I\'m connected!'}); }); </script>
也可用python客戶端連接
#-*-coding:utf-8-*- from socketIO_client import SocketIO, BaseNamespace import time class MonitorNamespace(BaseNamespace): def on_connect(self): print('[Connected]') def on_reconnect(self): print('[Reconnected]') def on_disconnect(self): print('[Disconnected]') socketIO = SocketIO('http://127.0.0.1', 5000) monitor_namespace = socketIO.define(MonitorNamespace, '/your_namespace') monitor_namespace.emit('join', {'room': 'your room'}) for i in range(30): monitor_namespace.emit('new_log', {'data': 'info ---{}'.format(i)}) time.sleep(1) monitor_namespace.emit('leave', {'room': 'your room'})
注意場景,send()
用於匿名事件; emit()
用於自定義事件。發送參數支持廣播broadcast=True和room='room_name'兩種。指定room后,只有加入到room的客戶端才能接收到消息。加入room的方法:
from flask_socketio import join_room, leave_room
send(username + ' has entered the room.', room=room)
訪問Flask全局變量
在調用事件函數前,應用上下文就已經被推入棧,以此確保current_app
和g
在事件函數中可用,在socket連接之前,或者發送消息之前:
with app.app_context():
init_database()
self.db = g.db
另外,request
多了sid
成員,用於為連接設定的session ID,其默認值為客戶端房間號。連接事件函數可以選擇返回False
來拒接連接請求。實際應用中,可以通過這種方式來驗證用戶權限。
部署
最簡單的部署方式,就是安裝eventlet或gevent,然后調用socketio.run(app)
gunicorn部署
gunicorn --worker-class eventlet -w 1 module:app
由於gunicorn算法的問題,服務器啟動只能使用一個worker進程。因此,必須加上-w 1
使用nginx作為WebSocket反向代理
server { listen 80; server_name _; location / { include proxy_params; proxy_pass http://127.0.0.1:5000; } location /socket.io { include proxy_params; proxy_http_version 1.1; proxy_buffering off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_pass http://127.0.0.1:5000/socket.io; } }
上述的ip和端口地址根據自己gunicorn設置的來定。
如果服務端啟用了ssl,socket連接時端口號要改為443。