WebSocket with Flask

转自:https://blog.shonenada.com/post/websocket-with-flask/

WebSocket with Flask

HTML5 以前,HTML 还不支持 WebSocket ,当时如果要进行实时的内容更新,要么使用 Ajax轮询(Polling)或者使用 Comet 技术。

Non-Websocket

Ajax 轮询

在 2005 年, Jesse James Garrett 提出 Ajax (Asynchronous JavaScript and XML, 异步 Javascript 和 XML)。具体请看Ajax: A New Approach to Web Applications 。并且从那时开始流行使用 Ajax 进行异步处理客户端请求。【关于异步处理请求的历史,可以看 http://en.wikipedia.org/wiki/Ajax_(programming) 中相关的介绍】。 XMLHttpRequest 在后台对服务器发起 request ,当收到 response 的时候,进行 DOM 操作,从而达到部分更新页面内容的目的(而不需要整个页面刷新)。

Ajax 轮询 可以做到接近实时的更新内容,但是因为是由客户端发起请求,即服务器处于被动的状态,这种“实时”存在缺陷: (1) 伪实时。服务器有更新的时候,只有客户端发起请求,服务器才能将更新返回到客户端。 (2) 数据更新量少的时候,容易造成浪费带宽、流量。 (3) 请求频率难以把握。太快会对服务器造成过大的压力,而太慢又不够“实时”,权衡频率需要考虑的因素很多。

Comet 技术

Comet 是指不需要客户端浏览器安装任何插件,仅靠浏览器和服务器之间的长 HTTP 连接实现服务器向客户端通信(服务器推)的技术。 Comet 有两种方式: Ajax长轮询 和 iframe with htmlfile stream

iframe with htmlfile streaming

这种技术,暂时没使用过。基本原理是使用 iframe 标签在 html 中插入一个隐藏的帧,向服务器建立长连接,服务器不断地向 iframe 输入数据。

Ajax 长轮询

Ajax 长轮询本质上也是 Ajax 轮询,不同的是,在服务器端做了些修改。当服务器没有更新的时候,服务器将请求阻塞,直到 有更新 或 连接超时。当请求结束之后再进行第二次的请求。

这种方式基本上可以避开 Ajax 轮询的缺陷。 Tornado 框架中的 Asynchronous 功能就是通过阻塞请求实现异步更新。 通过 Tornado 框架提供的 Asynchronous 功能可以实现实时数据传递。欢迎参考我在学习使用 tornado 异步功能时实现的两段应用:

  1. https://github.com/shonenada/chat-in-command-line
  2. https://github.com/shonenada/guess-number // 这程序功能不完善,但实现了异步的功能。

WebSocket

WebSocket 是 HTML5 的新功能,它是一种 TCP 协议。当客户端和服务器完成握手,建立连接之后,ws 就如普通 socket 一样,在两者之间进行通信。

理解了基本通信原理,就可以进行编程了。

前面已说,WS 是一种 TCP 协议,所以是语言无关的,用任何语言都可以实现服务器端的编程。我选择了 Python,使用 _flask: http://flask.pocoo.org/ 作为框架,以 _Gevent: http://www.gevent.org/ 和 _gevent-websocket:https://pypi.python.org/pypi/gevent-websocket/ 做 HttpServer。

实时更新基本的实现思路:

  1. 客户端发起 ws 连接请求
  2. 服务器响应,并且把 ws 加入到 observer 数组中。
  3. 当某一 ws 向服务器发送信息时,服务器遍历 observers 数组向每一个元素发送信息。
  4. ws 断开连接时,从 observer 中剔除。

具体实现代码:

# manage.py
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from flask import Flask, request, render_template, abort

import message

msgsrv = message.MessageServer()

app = Flask(__name__)

@app.route(‘/‘)
def index():
    return render_template(‘message.html‘)

@app.route(‘/message/‘)
def message():
    if request.environ.get(‘wsgi.websocket‘):
        ws = request.environ[‘wsgi.websocket‘]
        msgsrv.observers.append(ws)
        while True:
            if ws.socket:
                message = ws.receive()
                if message:
                    msgsrv.add_message("%s" % message)
            else:
                abort(404)
    return "Connected!"

if __name__ == ‘__main__‘:
    http_server = WSGIServer((‘‘, 5000), app, handler_class=WebSocketHandler)
    http_server.serve_forever()
# message.py
from geventwebsocket import WebSocketError

class MessageServer(object):

    def __init__(self):
        self.observers = []

    def add_message(self, msg):
        for ws in self.observers:
            try:
                ws.send(msg)
            except WebSocketError:
                self.observers.pop(self.observers.index(ws))
                print ws, ‘is closed‘
                continue
<!-- templates/message.html -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta http-equiv="Content-Language" content="zh-CN"/>
        <title>实时消息</title>
        <link rel="stylesheet" href="http://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script type="text/javascript" charset="utf-8">
        $(document).ready(function(){
            $(‘form‘).submit(function(event){
                ws.send($(this).serialize());
                return false;
            });
            if ("WebSocket" in window) {
                ws = new WebSocket("ws://" + document.domain + ":5000/message/");
                ws.onmessage = function (msg) {
                    console.log(msg.data);
                };
            } else {
                alert("WebSocket not supported");
            }
            window.onbeforeunload = function() {
                ws.onclose = function () {
                    console.log(‘unlodad‘)
                };
                ws.close()
            };
        });
        </script>
    </head>
    <body>
        <div class="header container">
            <h1>实时消息</h1>
            <ul class="tabs">
                <li class="active">
                    <a href="/">DEMO</a>
                </li>
            </ul>
        </div>
        <div class="container">
            Pls check your Chrome console.
            <form class="row" id="message_form">
                <div class="span10">
                    <div class="clearfix">
                        <label for="chat_content">消息</label>
                        <div class="input">
                            <textarea id="chat_content" name="content" class="xlarge" rows="6"></textarea>
                        </div>
                    </div>
                    <div class="well align-center">
                        <input type="submit" class="btn primary" value="发布">
                        &nbsp;
                        <input type="reset" class="btn" value="清空">
                    </div>
                </div>
            </form>
        </div>
        <div class="footer container">
            <p>
                &copy; Copyright by shonenada
            </p>
        </div>
    </body>
</html>

~接下来可以实现 HTML5 的桌面直播了。

时间: 2024-10-10 14:33:19

WebSocket with Flask的相关文章

websocket 群聊,单聊,加密,解密

群聊 from flask import Flask, request, render_templatefrom geventwebsocket.handler import WebSocketHandlerfrom gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket app = Flask(__name__) # type:Flask user_socket_list = [] @app

轮询、长轮询和websocket

一.轮询 在一些需要进行实时查询的场景下应用比如投票系统: 大家一起在一个页面上投票 在不刷新页面的情况下,实时查看投票结果 1.后端代码 from flask import Flask, render_template, request, jsonify app = Flask(__name__) USERS = { 1: {'name': '明凯', 'count': 300}, 2: {'name': '厂长', 'count': 200}, 3: {'name': '7酱', 'coun

Flask实现群聊

后端 from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket from flask import Flask,request,render_template user_socket_list = [] app = Flask(__name__) @app.route("/conn

Websocket 单聊功能

单聊代码 import json from flask import Flask,request,render_template from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket app = Flask(__name__) user_socket_dict = {} @ap

基于flask 写的web_socket 单聊和群聊

群聊 from flask import Flask,request,render_template from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer from geventwebsocket.websocket import WebSocket app = Flask(__name__) user_socket_list = [] @app.route("/conn

人工智能语音识别系统开发

1.初始教程 1.登录百度云管理中心,点击管理控制台 2.进入后,下拉在已开通服务中选择百度语音 3.点击创建应用,得到以下应用 4.这里也可以查看它的相关技术文档 快速入门链接 2.文字合成语音 1.安装使用Python SDK,终端下: pip install baidu-aip 技术文档链接 使用 from aip import AipSpeech """ 你的 APPID AK SK """ APP_ID = '11793737' API

图灵机器人,web录音实现自动化交互问答

一.图灵机器人 介绍 图灵机器人是以语义技术为核心驱动的人工智能公司,致力于"让机器理解世界",产品服务包括机器人开放平台.机器人os和场景方案.\ 官方地址为: http://www.tuling123.com/ 使用 首先得注册一个账号,或者使用第3方登录,都可以. 登录之后,点击创建机器人 机器人名称,可以是自己定义的名字 选择网站->教育学习->其他 输入简介 创建成功之后,点击终端设置,拉到最后. 可以看到api接入,下面有一个apikey,待会会用到 右侧有一个

Flask------一些面试题

1.请手写一个flask的hello world. hello.py中 #coding = utf-8 # 第一步:导入Flask类 from flask import Flask app = Flask(__name__) @app.route("/") def helloworld(): return "<h1>hello world</h1>" if __name__ == " __main__": app.run(

websocket-单群聊

简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议. WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输. Web - Socket  客户端 与 服务器 达成一个协议websocket 客户端 与 websocket 服务器 达成一个websocket协议 来个小例子方便大家理解: ws-socket.py fr