WebSocket
WebSocket Protocol
Documentation: https://tools.ietf.org/html/rfc6455
HTTP has historically been a stateless, unidirectional protocol — the client sends a request and the server responds once. By default, only the browser can initiate a request; the server cannot push data on its own. To deliver real-time server messages to the client, developers had to rely on polling mechanisms: the client would use a timer to repeatedly send AJAX requests to the server. This approach is inefficient, and the HTTP packet headers themselves consume significant bandwidth and server resources.
To improve efficiency, HTML5 introduced WebSocket technology.
WebSocket is a technology that enables full-duplex communication between a client and a server. It is a protocol specification in the latest HTML5 standard, and is essentially a TCP-based protocol. It establishes a TCP connection by sending a special handshake request over HTTP/HTTPS, after which the browser/client and server can exchange bidirectional real-time messages at any time over that connection, with very small packet overhead.
HTML5 also provides a simple API so that front-end developers can implement socket communication directly. Developers only need to implement four events — onopen, onmessage, onclose, and onerror — in a WebSocket-supporting browser.
Note: WebSocket is part of HTML5, but it is not limited to browsers or HTML documents. As long as a language (such as Python or C++) can implement the WebSocket protocol framing, it can be used.
Reference: https://blog.csdn.net/zhusongziye/article/details/80316127
Client request frame:
GET /mofang/websocket HTTP/1.1
Host: 127.0.0.1
Origin: http://127.0.0.1:5000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== # Sec-WebSocket-Key is randomly generated
Sec-WebSocket-Version: 13Server response frame:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= # Computed from the client's Sec-WebSocket-Key using a fixed algorithm
Sec-WebSocket-Protocol: chatRelationship Between WebSocket and Socket
The relationship between the two is like Java and JavaScript — not entirely unrelated, but not the same thing either.
Strictly speaking, Socket is not a protocol; it is a set of interfaces that wrap TCP/IP to make it easier for developers to use TCP or UDP. It sits between the application layer and the transport layer.
WebSocket is a full-duplex communication protocol between browsers and servers — an application-layer protocol that simulates socket behavior.
Server-Side Socket Service
There are many ways to implement a socket server in Python. One common choice is python-socketio. The Flask framework has a corresponding wrapper called flask-socketio.
Official documentation: https://flask-socketio.readthedocs.io/en/latest/
Because a small fraction of devices or applications still do not support WebSocket, we use socketio to ensure compatibility. This introduces two important constraints:
If the Python server uses socketio for communication, the other end must also use socketio; otherwise communication will fail.
There is also a version compatibility requirement. Mismatched versions prevent communication and result in version errors.
- If the JavaScript side uses socket.io 1.x or 2.x, the Python side must use python-socketio or flask-socketio 4.x.
- If the JavaScript side uses socket.io 3.x, the Python side must use python-socketio or flask-socketio 5.x.
We currently use flask-socketio 5.x, so the JavaScript socket.io version must be 3.x.
Install via terminal:
pip install flask-socketio
pip install gevent-websocketModule initialization, application/__init__.py:
import os,sys
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
from flask_session import Session
from flask_migrate import Migrate,MigrateCommand
from flask_jsonrpc import JSONRPC
from flask_marshmallow import Marshmallow
from flask_jwt_extended import JWTManager
from flask_admin import Admin
from flask_babelex import Babel
from faker import Faker
from flask_pymongo import PyMongo
from flask_qrcode import QRcode
from flask_socketio import SocketIO
from application.utils import init_blueprint
from application.utils.config import load_config
from application.utils.session import init_session
from application.utils.logger import Log
from application.utils.commands import load_command
# Create terminal script manager object
manager = Manager()
# Create database connection object
db = SQLAlchemy()
# Redis connection object
redis = FlaskRedis()
# Session storage object
session_store = Session()
# Database migration instance
migrate = Migrate()
# Logger object
log = Log()
# JSON-RPC module instance
jsonrpc = JSONRPC()
# Data serializer object
ma = Marshmallow()
# JWT authentication module instance
jwt = JWTManager()
# flask-admin module instance
admin = Admin()
# flask-babelex module instance
babel = Babel()
# MongoDB
mongo = PyMongo()
# QR code
QRCode = QRcode()
# SocketIO
socketio = SocketIO()
def init_app(config_path):
"""Global initialization"""
# Create Flask app object
app = Flask(__name__)
# Project root directory
app.BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Add import path
sys.path.insert(0, os.path.join(app.BASE_DIR,"application/utils/language"))
# Load configuration
Config = load_config(config_path)
app.config.from_object(Config)
# Database initialization
db.init_app(app)
app.db = db
redis.init_app(app)
mongo.init_app(app)
# Data serializer initialization
ma.init_app(app)
# Session storage initialization
init_session(app)
session_store.init_app(app)
# Database migration initialization
migrate.init_app(app,db)
# Add migration command to terminal script manager
manager.add_command('db', MigrateCommand)
# Logger initialization
app.log = log.init_app(app)
# Blueprint registration
init_blueprint(app)
# JSON-RPC initialization
jsonrpc.service_url = "/api" # API URL prefix
jsonrpc.init_app(app)
# JWT initialization
jwt.init_app(app)
# Admin initialization
admin.init_app(app)
# Internationalization/localization module initialization
babel.init_app(app)
# Initialize terminal script manager
manager.app = app
# Data seed generator [faker]
app.faker = Faker(app.config.get("LANGUAGE"))
# QR code initialization
QRCode.init_app(app)
# SocketIO initialization
socketio.init_app(app, cors_allowed_origins=app.config["CORS_ALLOWED_ORIGINS"],async_mode=app.config["ASYNC_MODE"], debug=app.config["DEBUG"])
# Override runserver command
if sys.argv[1] == "runserver":
manager.add_command("run", socketio.run(app,host=app.config["HOST"],port=app.config["PORT"]))
# Register custom commands
load_command(manager)
return managerConfiguration file, application/settings/dev.py:
# socketio
CORS_ALLOWED_ORIGINS="*"
ASYNC_MODE=None
HOST="0.0.0.0"
PORT=5000application/utils/__init__.py — during blueprint loading, automatically load the socket server API:
def init_blueprint(app):
"""Auto-register blueprints"""
blueprint_path_list = app.config.get("INSTALLED_APPS")
# Load admin site master config
try:
import_module(app.config.get("ADMIN_PATH"))
except:
pass
for blueprint_path in blueprint_path_list:
blueprint_name = blueprint_path.split(".")[-1]
# Auto-create blueprint object
blueprint = Blueprint(blueprint_name,blueprint_path)
# Auto-register blueprint and bind views and sub-routes
url_module = import_module(blueprint_path+".urls") # Load sub-routes file under blueprint
for url in url_module.urlpatterns: # Iterate over all route mappings in sub-routes
blueprint.add_url_rule(**url) # Register to blueprint
# Read master URL file
url_path = app.config.get("URL_PATH")
urlpatterns = import_module(url_path).urlpatterns # Load sub-routes
url_prefix = "" # Blueprint route prefix
for urlpattern in urlpatterns:
if urlpattern["blueprint_path"] == blueprint_name+".urls":
url_prefix = urlpattern["url_prefix"]
break
# Register models
import_module(blueprint_path+".models")
# Load blueprint-level admin site config
try:
import_module(blueprint_path+".admin")
except:
pass
# Load blueprint-level socket interfaces
try:
import_module(blueprint_path+".socket")
except:
pass
# Register blueprint to app, with url_prefix as the route prefix
app.register_blueprint(blueprint,url_prefix=url_prefix)Because the server is based on python-socketio, the client must use socketIO.js to communicate with it.
socket.io.js official docs: https://socket.io/docs/v3
socket.io.js GitHub: https://github.com/socketio/socket.io/releases
We can create orchard.html as the main page for the orchard module and use socketio to communicate with the Flask-SocketIO server.
Code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="../static/js/socket.io.js"></script>
</head>
<body>
<script>
// Namespace
namespace = '/mofang';
var socket = io.connect('ws://192.168.20.251:5000' + namespace, {transports: ['websocket']});
// socket.on('connect', function() {
// console.log("Client connected to socket server");
// });
</script>
</body>
</html>Create and register the orchard blueprint directory from the terminal:
cd application/apps/
python ../../manage.py blue -n=orchardapplication/urls.py:
from application.utils import include
urlpatterns = [
include("","home.urls"),
include("/users","users.urls"),
include("/marsh","marsh.urls"),
include("/orchard","orchard.urls"),
]application/settings/dev.py:
# Registered blueprints
INSTALLED_APPS = [
"application.apps.home",
"application.apps.users",
"application.apps.marsh",
"application.apps.orchard",
]Creating a Socket Connection
Create socket.py under the blueprint and provide connection interfaces, orchard/socket.py:
from application import socketio
from flask import request
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid is the unique session ID generated by SocketIO for each client
print("User %s connected!" % request.sid)
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("User %s left the orchard" % request.sid)Client Integration: Vue + SocketIO
Client code:
<!DOCTYPE html>
<html>
<head>
<title>User Center</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// Socket connection
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("Connecting to server");
});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>CSS styles, main.css:
.app .orchard-bg{
margin: 0 auto;
width: 100%;
max-width: 100rem;
position: absolute;;
z-index: -1;
top: -6rem;
}
.app .orchard-bg .board_bg2{
position: absolute;
top: 1rem;
}
.orchard .back{
position: absolute;
width: 3.83rem;
height: 3.89rem;
z-index: 1;
top: 2rem;
left: 2rem;
}
.orchard .music{
right: 2rem;
}
.orchard .header{
position: absolute;
top: 0rem;
left: 0;
right: 0;
margin: auto;
width: 32rem;
height: 19.28rem;
}
.orchard .info{
position: absolute;
z-index: 1;
top: 0rem;
left: 4.4rem;
width: 8rem;
height: 9.17rem;
}
.orchard .info .avata{
width: 8rem;
height: 8rem;
position: relative;
}
.orchard .info .avatar_bf{
position: absolute;
z-index: 1;
margin: auto;
width: 6rem;
height: 6rem;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.orchard .info .user_avatar{
position: absolute;
z-index: 1;
width: 6rem;
height: 6rem;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 1rem;
}
.orchard .info .avatar_border{
position: absolute;
z-index: 1;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 7.2rem;
height: 7.2rem;
}
.orchard .info .user_name{
position: absolute;
left: 8rem;
top: 1rem;
width: 11rem;
height: 3rem;
line-height: 3rem;
font-size: 1.5rem;
text-shadow: 1px 1px 1px #aaa;
border-radius: 3rem;
background: #ff9900;
text-align: center;
}
.orchard .wallet{
position: absolute;
top: 3.4rem;
right: 4rem;
width: 16rem;
height: 10rem;
}
.orchard .wallet .balance{
margin-top: 1.4rem;
float: left;
margin-right: 1rem;
}
.orchard .wallet .title{
color: #fff;
font-size: 1.2rem;
width: 6.4rem;
text-align: center;
}
.orchard .wallet .title img{
width: 1.4rem;
margin-right: 0.2rem;
vertical-align: sub;
height: 1.4rem;
}
.orchard .wallet .num{
background: url("../images/btn3.png") no-repeat 0 0;
background-size: 100%;
width: 6.4rem;
font-size: 0.8rem;
color: #fff;
height: 2rem;
line-height: 1.8rem;
text-indent: 1rem;
}
.orchard .header .menu-list{
position: absolute;
top: 9rem;
left: 2rem;
}
.orchard .header .menu-list .menu{
color: #fff;
font-size: 1rem;
float: left;
width: 4rem;
height: 4rem;
text-align: center;
margin-right: 2rem;
}
.orchard .header .menu-list .menu img{
width: 3.33rem;
height: 3.61rem;
display: block;
margin: auto;
margin-bottom: 0.4rem;
}
.orchard .footer{
position: absolute;
width: 100%;
height: 6rem;
bottom: -2rem;
background: url("../images/board_bg3.png") no-repeat -1rem 0;
background-size: 110%;
}
.orchard .footer .menu-list{
width: 100%;
height: 4rem;
display: flex;
position: absolute;
top: -1rem;
}
.orchard .footer .menu-list .menu,
.orchard .footer .menu-list .menu-center{
float: left;
width: 4.44rem;
height: 5.2rem;
font-size: 1.5rem;
color: #fff;
line-height: 4.44rem;
text-align: center;
background: url("../images/btn5.png") no-repeat 0 0;
background-size: 100%;
flex: 1;
margin-left: 4px;
margin-right: 4px;
}
.orchard .footer .menu-list .menu-center{
background: url("../images/btn6.png") no-repeat 0 0;
background-size: 100%;
flex: 2;
}Receiving Messages via Events
Communication Using Unnamed Events
Client code:
<!DOCTYPE html>
<html>
<head>
<title>User Center</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// Socket connection
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("Connecting to server");
this.login();
});
},
login(){
var id = this.game.fget("id");
// Use send() to transmit data without specifying an event name; data must be JSON
this.socket.send({"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>Server code:
from application import socketio
from flask import request
# Establish socket connection
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid is the unique session ID generated by SocketIO for each client
print("User %s connected!" % request.sid)
# Disconnect socket
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("User %s left the orchard" % request.sid)
# Unnamed event — client did not specify an event name
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("Received data from %s:" % request.sid)
print(data)
print(data["uid"])Communication Using Custom Events
Client code:
<!DOCTYPE html>
<html>
<head>
<title>User Center</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// Socket connection
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("Connecting to server");
this.login();
});
},
login(){
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>Server code:
from application import socketio
from flask import request
# Establish socket connection
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid is the unique session ID generated by SocketIO for each client
print("User %s connected!" % request.sid)
# Disconnect socket
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("User %s left the orchard" % request.sid)
# Unnamed event
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("Received data from %s:" % request.sid)
print(data)
print(data["uid"])
# Custom event
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("Received data from client %s:" % request.sid)
print(data)
print(data["uid"])Server-Side Response
from application import socketio
from flask import request
from application.apps.users.models import User
# Establish socket connection
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid is the unique session ID generated by SocketIO for each client
print("User %s connected!" % request.sid)
# Actively push data to the client
length = User.query.count()
socketio.emit("server_response",{"count":length},namespace="/mofang")
# Disconnect socket
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("User %s left the orchard" % request.sid)
# Unnamed event
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("Received data from %s:" % request.sid)
print(data)
print(data["uid"])
# Custom event
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("Received data from client %s:" % request.sid)
print(data)Client receiving server responses:
<!DOCTYPE html>
<html>
<head>
<title>User Center</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// Socket connection
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("Connecting to server");
this.login();
this.get_count();
});
},
get_count(){
this.socket.on("server_response",(res)=>{
this.game.print(res.count);
alert(`Welcome to the orchard, there are currently ${res.count} people active~`)
});
},
login(){
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>Room-Based Message Distribution
from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
# Establish socket connection
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid is the unique session ID generated by SocketIO for each client
print("User %s connected!" % request.sid)
# Actively push data to the client
length = User.query.count()
socketio.emit("server_response",{"count":length,"sid":"%s"% request.sid},namespace="/mofang")
# Disconnect socket
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("User %s left the orchard" % request.sid)
# Unnamed event
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("Received data from %s:" % request.sid)
print(data)
print(data["uid"])
# Custom event
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("Received data from client %s:" % request.sid)
print(data)
# Typically assign rooms based on user ID
room = data["uid"]
join_room(room)
socketio.emit("login_response", {"data": "Login successful"}, namespace="/mofang", room=room)Client code:
<!DOCTYPE html>
<html>
<head>
<title>User Center</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// Socket connection
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("Connecting to server");
this.login();
this.get_count();
this.login_response();
});
},
login_response(){
this.socket.on("login_response",(res)=>{
alert(res.data);
});
},
get_count(){
this.socket.on("server_response",(res)=>{
this.game.print(res.count);
alert(`Welcome ${res.sid} to the orchard, there are currently ${res.count} people active~`);
});
},
login(){
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>Server-Side Scheduled Push
from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
# Establish socket connection
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid is the unique session ID generated by SocketIO for each client
print("User %s connected!" % request.sid)
# Actively push data to the client
length = User.query.count()
socketio.emit("server_response",{"count":length,"sid":"%s"% request.sid},namespace="/mofang")
# Disconnect socket
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("User %s left the orchard" % request.sid)
# Unnamed event
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("Received data from %s:" % request.sid)
print(data)
print(data["uid"])
# Custom event
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("Received data from client %s:" % request.sid)
print(data)
# Typically assign rooms based on user ID
room = data["uid"]
join_room(room)
socketio.emit("login_response", {"data": "Login successful"}, namespace="/mofang", room=room)
"""Scheduled data push"""
from threading import Lock
import random
thread = None
thread_lock = Lock()
@socketio.on('chat', namespace='/mofang')
def chat(data):
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(target=background_thread)
def background_thread(uid):
while True:
socketio.sleep(1)
t = random.randint(1, 100)
socketio.emit('server_response',
{'count': t},namespace='/mofang')Client code:
<!DOCTYPE html>
<html>
<head>
<title>User Center</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<h1 style="position:absolute;top:20rem;">{{num}}</h1>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
num:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// Socket connection
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("Connecting to server");
this.login();
this.get_count();
this.login_response();
});
},
login_response(){
this.socket.on("login_response",(res)=>{
alert(res.data);
});
},
get_count(){
this.socket.on("server_response",(res)=>{
this.num = res.count;
// alert(`Welcome ${res.sid} to the orchard, there are currently ${res.count} people active~`);
});
},
login(){
var id = this.game.fget("id");
// this.socket.emit("login",{"uid":id});
this.socket.emit("chat",{"uid":id})
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>Server-Side Broadcast
from flask_socketio import emit
@socketio.on('my_broadcast', namespace='/mofang')
def my_broadcast(data):
emit('broadcast_response', data, broadcast=True)
socketio.emit('some event', {'data': 42})
# Without specifying a room ID, the message is delivered to all users in the namespace by default