Skip to content

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: 13

Server 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: chat

Relationship 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:

  1. If the Python server uses socketio for communication, the other end must also use socketio; otherwise communication will fail.

  2. 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-websocket

Module 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 manager

Configuration file, application/settings/dev.py:

    # socketio
    CORS_ALLOWED_ORIGINS="*"
    ASYNC_MODE=None
    HOST="0.0.0.0"
    PORT=5000

application/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=orchard

application/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
Last updated on