websocket的简单了解和python应用

web时代,http协议是当之无愧的幸运儿,它是在TCP传输层协议的基础上,建立的web应用层协议,主要进行html文本的传输,其他的都能算是http当中元素的渲染。早期只是简单的html文本,后续引入css优化了样式,美化了显示,然后的js又加强了动态渲染,而后后续的种种web框架在提升程序员开发效率的同时,也进行各种安全和性能上的优化。但有一点不变的是,http服务端只能被动地去接收来自客户端的请求,在经历后台处理以后,再做出响应。

针对这种被动的单向请求,当服务器存在信息变更的时候,想要主动推送信息给客户就非常麻烦。为此而生的技术,有js种的自动请求,在构建网页的时候,就事先做好了js设定,在一定情况下或者时间内,组织客户端技术发起请求;但这种技术能用,但不够美观,效率也没有跟上预期,在设计上更理想的就是,服务器在信息进行变更的情况下,马上推送更新后信息给客户端,而websocket就应了这种需求而诞生。

实现

websocket是单个TCP连接上全双工通信,在客户端和服务端进行一次握手后,就可以实现持续连接,从而进行数据的双向传输。在客户端或者服务端任意一方发起建立websocket通信的请求以后,对端进行确认式回应,然后一个类似tcp的连接建立,因此常用于IM通信的情景。

websocket和http相同的地方,都是基于TCP的可靠性传输的应用层协议;不同的是websocket是双向的,http是单向的,而且websocket通信前,需要发起一个握手连接,而客户端在http请求发起前,服务端并不知道对端存在。

实现原理:在websocket通信前,通信是http形式,而两者都是基于TCP连接的,所以websocket连接的握手,客户端就是在TCP连接建立以后,在http头中放进去特定的信息,如下:

GET /chat HTTP/1.1
Host: www.example.com
Upgrade: websocket
Connection: Upgrade

上面表示客户端准备升级连接为长连接的websocket,而服务器的成功回应是这样的:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

响应成功以后,tcp连接就这么长久维持,双方传输的信息大多是json格式的文本信息。因为是基于TCP的通信,所以它的各种特点都是和tcp相仿的。

websocket在flask中的应用

flask是一个轻量级的web框架,它本身可以提供简单的web api,但如果想要更多的功能,就需要自己去寻找插件的支持了,也是因为这些丰富的插件,才使得flask应用广泛。websocket在flask中的应用,就是flask-sockets和flask_socketIO,前者是对websocket的简单实现,对于已经实现了websocket支持的浏览器友好,丑拒旧版不支持websocket的浏览器;后者的功能是前者的丰富和补全,因为它支持旧版,能做到这点,就已经很足够了。

flask_sockets使用

进行试验以前,当然要进行经典的pip:

pip install flask
pip install flask-cors
pip install flask-sockets

示例如下:

from flask import Flask, render_template
from flask_sockets import Sockets
from flask_cors import *
from time import sleep


app = Flask(__name__)
socket = Sockets(app)
CORS(app, supports_credentials=True)

# 返回一个内含websocket请求发起js的html
@app.route('/')
def home():
    return render_template('index.html')

@socket.route('/hello')
def hello(ws):
    print('websocket处理')
    while not ws.closed():
        ws.send("你大爷")
        sleep(3)


if __name__ == '__main__':
    from gevent import pywsgi
    from geventwebsocket.handler import WebSocketHandler
    server = pywsgi.WSGIServer(('',8090), app, handler_class=WebSocketHandler)
    print('服务开启监听')
    server.serve_forever()

主要是给一个主页面,使其返回一个内含链接的html页面,它内含js,在点击跳转的时候可以进行websocket的请求构造:

<!DOCTYPE html>
<html>
    <body>
        <div align="center">
            <h1>Welcome,My Friend.</h1>
            <a href="javascript:enter()">来进入下一个领域吧</a>
        </div>
    </body>
    <script>
        function enter(){
            if ('WebSocket' in window){
                // 创建websocket即发起请求
                var web_socket = new WebSocket("ws://localhost:8090/hello")

                // 设置onmessage事件的回调,也就是接收数据
                web_socket.onmessage = function(event){
                    console.log(event.data)
                }
                // 设置onopen的回调
                web_socket.onopen = function(){
                    console.log("开始连接")
                }
                // 设置onclose的回调
                web_socket.onclose = function(event) {
                    console.log('连接结束:'+event.code)
                }
            } else {
                alert('浏览器不支持websocket')
            }
        }
    </script>
</html>

当然,设置一个带有点击事件的button也不是不可以。

...
            <button onclick="enter(this)">来进入下一个领域吧</button>
...

去前台f12一下,可以看到hello请求的请求头:

请求头
和响应:
响应头

flask-socketIO使用

使用前同样需要安装一下:

pip install flask
pip install flask-socketIO

实例:

from flask import Flask, render_template
from flask_socketio import SocketIO, emit


app = Flask(__name__)
app.config['SECRET_KEY'] = 'test'

socketIo = SocketIO()
# 添加cors支持,解决跨域问题
socketIo.init_app(app, cors_allowed_origin='*')
name_space = '/hello'

@app.route('/')
def home():
    return render_template('index.html')

@app.route('/push')
def push_msg():
    event_name = 'hello'
    broadcasted_data = {'data':'test'}
    # 广播信息
    socketIo.emit(event_name, broadcasted_data, broadcast=False, namespace=name_space)
    return 'over'

# 设置websocket连接建立后回调
@socketIo.on('connect', namespace=name_space)
def connect():
    print(f'websocket连接已建立,namespace:{name_space}')

# 设置websocket连接断开后回调
@socketIo.on('disconnect', namespace=name_space)
def disconnect():
    print(f'websocket连接已断开,namespace:{name_space}')

@socketIo.on('event1', namespace=name_space)
def test(msg):
    print(f'客户端:{msg}')
    emit('response', {'data':msg['data'], 'count':1})


if __name__ == '__main__':
    # 不像往常的app.run,这里不显示运行所在
    socketIo.run(app,host='0.0.0.0', port=8090)

在js中,使用jquery也可以构建websocekt通信,下面有一个加载ajax的例子,就是在加载了html以后,它的js自动请求后续内容,进行渲染:

<script>
$(document).ready(function () {
    namespace = '/hello';
    var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
    socket.on('hello', function (res) {
        var data_show = res.data;
        if (data_show) {
            $("#data_show").append(data_show).append('<br/>');
        }
    });
});
</script>

但在实践的时候,却问题百出,一个是没有引入js库

<header>
    <script src="https://lib.baomitu.com/jquery/1.12.4/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
</header>

一个是python的flask-socketIO、python-socketIO和python-engineIO版本不搭,使用下面图片对标的版本进行安装,后面又有其他问题ImportError: cannot import name 'run_with_reloader' from 'werkzeug.serving',所以先放着吧。

socketIO版本搭配

更多内容可以查看

posted @ 2023-06-04 10:35  夏目&贵志  阅读(329)  评论(0)    收藏  举报