WebSocket在Python中的应用

前言

WebSocket与HTTPS相似,是一种客户端-服务端模型的通信协议。

HTTP carries extra overheard in individual request and response, WebSocket carries the overhead data while placing connection then it carries less or balanced data within individual request-response.

HTTP WebSocket
Added overhead due to SSL handshake and likewise carries added overhead in individual request-response Lessor moderate overhead while establishing the connection and less overhead in individual requests
The client needs to interact with the server via a specific method for communication or transmission of data WebSocket is bi-directional
Half-Duplex Full-Duplex
Service push is not supported natively, client pulling or download streaming is required The Service push is base of WebSocket implementation

Socket.IO

socket.io是一个基于WebSocket的CS的实时通信库,它底层基于engine.io。engine.io使用WebSocket和xhr-polling(或jsonp)封装了一套自己的协议,在不支持WebSocket的低版本浏览器中来代替。socket.io在engine.io的基础上增加了namespace,room,自动重连等特性。

在HTTP 协议开发的时候,并不是为了双向通信程序准备的,起初的 web 应用程序只需要 “请求-响应” 就够了。由于历史原因,在创建拥有双向通信机制的 web 应用程序时,就只能利用 HTTP 轮询的方式,由此产生了 “短轮询” 和 “长轮询”(注意区分短连接和长连接)。

短轮询通过客户端定期轮询来询问服务端是否有新的信息产生,缺点也是显而易见,轮询间隔大了则信息不够实时,轮询间隔过小又会消耗过多的流量,增加服务器的负担。长轮询是对短轮询的优化,需要服务端做相应的修改来支持。客户端向服务端发送请求时,如果此时服务端没有新的信息产生,并不立刻返回,而是Hang住一段时间等有新的信息或者超时再返回,客户端收到服务器的应答后继续轮询。可以看到长轮询比短轮询可以减少大量无用的请求,并且客户端接收取新消息也会实时不少。

虽然长轮询比短轮询优化了不少,但是每次请求还是都要带上HTTP请求头部,而且在长轮询的连接结束之后,服务器端积累的新消息要等到下次客户端连接时才能传递。更好的方式是只用一个TCP连接来实现客户端和服务端的双向通信,WebSocket协议正是为此而生。WebSocket是基于TCP的一个独立的协议,它与HTTP协议的唯一关系就是它的握手请求可以作为一个Upgrade request经由HTTP服务器解析,且与HTTP使用一样的端口。WebSocket默认对普通请求使用80端口,协议为ws://,对TLS加密请求使用443端口,协议为wss://

更多详细内容可以查看参考[2]

Flask Sockets

接下来我们使用Flask-SocketIO来创建一个Socketio的Demo。具体文档可以参考:Flask-SocketIO,代码可以参考:Web Socket

  1. 创建环境
    使用conda命令创建一个新的环境,然后安装如下依赖:
Flask==1.0.2
Flask-Login==0.4.1
Flask-Session==0.3.1
Flask_SocketIO
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.0
python-engineio
python-socketio
six==1.11.0
Werkzeug==0.14.1
  1. 创建应用

创建app.py

from flask import Flask, render_template
from flask_socketio import SocketIO
async_mode = None
app = Flask(__name__)
socket_ = SocketIO(app, async_mode=async_mode)
@app.route('/')
def index():
    return render_template('index.html',
                           sync_mode=socket_.async_mode)


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

创建前端测试页面index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket</title>
</head>
<body>
<h1>This is webSocket client</h1>
</body>
</html>
  1. 运行app.py
    运行app.py后在浏览器中访问:http://localhost:5000/ ,成功的话会在前端页面上有提示信息:This is WebSocket Client。具体的工程目录如下:
    在这里插入图片描述

最终的app.pyindex.html代码如下:

from flask import Flask, render_template, session, copy_current_request_context
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock


async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()


@app.route('/')
def index():
    return render_template('index.html', async_mode=socket_.async_mode)


@socket_.on('my_event', namespace='/test')
def test_message(message):
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {
    
    'data': message['data'], 'count': session['receive_count']})


@socket_.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {
    
    'data': message['data'], 'count': session['receive_count']},
         broadcast=True)


@socket_.on('disconnect_request', namespace='/test')
def disconnect_request():
    @copy_current_request_context
    def can_disconnect():
        disconnect()

    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {
    
    'data': 'Disconnected!', 'count': session['receive_count']},
         callback=can_disconnect)


if __name__ == '__main__':
    socket_.run(app, debug=True)
<!DOCTYPE HTML>
<html>
<head>
    <title>Socket-Test</title>
    <script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {
      
      

            namespace = '/test';
            var socket = io(namespace);

            socket.on('connect', function() {
      
      
                socket.emit('my_event', {
      
      data: 'connected to the SocketServer...'});
            });

            socket.on('my_response', function(msg, cb) {
      
      
                $('#log').append('<br>' + $('<div/>').text('logs #' + msg.count + ': ' + msg.data).html());
                if (cb)
                    cb();
            });
            $('form#emit').submit(function(event) {
      
      
                socket.emit('my_event', {
      
      data: $('#emit_data').val()});
                return false;
            });
            $('form#broadcast').submit(function(event) {
      
      
                socket.emit('my_broadcast_event', {
      
      data: $('#broadcast_data').val()});
                return false;
            });
            $('form#disconnect').submit(function(event) {
      
      
                socket.emit('disconnect_request');
                return false;
            });
        });
    </script>
</head>
<body style="background-color:white;">

    <h1 style="background-color:white;">Socket</h1>
    <form id="emit" method="POST" action='#'>
        <input type="text" name="emit_data" id="emit_data" placeholder="Message">
        <input type="submit" value="Send Message">
    </form>
    <form id="broadcast" method="POST" action='#'>
        <input type="text" name="broadcast_data" id="broadcast_data" placeholder="Message">
        <input type="submit" value="Send Broadcast Message">
    </form>

    <form id="disconnect" method="POST" action="#">
        <input type="submit" value="Disconnect Server">
    </form>
    <h2 style="background-color:white;">Logs</h2>
    <div id="log" ></div>
</body>
</html>

参考

  1. Implement a WebSocket Using Flask and Socket-IO(Python)
  2. Socket.io原理分析

猜你喜欢

转载自blog.csdn.net/u012655441/article/details/124823542