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 异步功能时实现的两段应用:
- https://github.com/shonenada/chat-in-command-line
- 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。
实时更新基本的实现思路:
- 客户端发起 ws 连接请求
- 服务器响应,并且把 ws 加入到 observer 数组中。
- 当某一 ws 向服务器发送信息时,服务器遍历 observers 数组向每一个元素发送信息。
- 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="发布">
-
- <input type="reset" class="btn" value="清空">
- </div>
- </div>
- </form>
- </div>
- <div class="footer container">
- <p>
- © Copyright by shonenada
- </p>
- </div>
- </body>
- </html>
~接下来可以实现 HTML5 的桌面直播了。
WebSocket with Flask的更多相关文章
- python 全栈开发,Day139(websocket原理,flask之请求上下文)
昨日内容回顾 flask和django对比 flask和django本质是一样的,都是web框架. 但是django自带了一些组件,flask虽然自带的组件比较少,但是它有很多的第三方插件. 那么在什 ...
- flask总结之session,websocket,上下文管理
1.关于session flask是带有session的,它加密后存储在用户浏览器的cookie中,可以通过app.seesion_interface源码查看 from flask import Fl ...
- WebSocket 实现链接 群聊(low low low 版本)
py 文件: """ 下载 gevent-websocket 0.10.1 基于Flask + geventWebSocket 建立连接,发送消息,实现群消息功能. &q ...
- WebSocket 实现链接 发送消息
Websocket 原理浅析地址: https://www.cnblogs.com/yuanyongqiang/articles/10457793.html 直接上代码: myWebSocket.py ...
- websocket 群聊,单聊,加密,解密
群聊 from flask import Flask, request, render_templatefrom geventwebsocket.handler import WebSocketHan ...
- Websocket实现群聊、单聊
Websocket 使用的第三方模块:gevent-websocket 群聊 ws群聊.py中的内容 from flask import Flask, request, render_template ...
- flask+socketio+echarts3 服务器监控程序(基于后端数据推送)
本文地址:http://www.cnblogs.com/hhh5460/p/7397006.html 说明 以前的那个例子的思路是后端监控数据存入数据库:前端ajax定时查询数据库. 这几天在看web ...
- websocket实现群聊
server # @File: 群聊 from flask import Flask, render_template, request from geventwebsocket.handler im ...
- WebSocket 笔记
WebSocket介绍 WebSocket+Flask开启一个WebSocket服务 群聊小Demo 私聊小Demo WebSocket介绍 - 菜鸟教程详解连接 - 下载:pip install g ...
随机推荐
- CF 2013-2014CTS01E04(Killer Challenge-将质因数存在 进行Bitmask)
首先,把P进行质因数分解,每一个不用的质因数压成1位 f[i][j]表示1前i位用j“拥有”的质因数表示. 然后都懂得... #include<cstdio> #include<cs ...
- POJ 1904 HDU 4685
这两道题差不多,POJ这道我很久以前就做过,但是比赛的时候居然没想起来.. POJ 这道题的题意是,N个王子每个人都有喜欢的公主,当他们选定一个公主结婚时,必须是的剩下的人也能找到他喜欢的公主结婚. ...
- BZOJ 1041 圆上的整点
题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1041 题意:求圆x^2+y^2=r^2上的整点. 思路:由于对称性,我们只需要计算第一象 ...
- Server Profiler
Server Profiler 2014-10-31 工作原理 SQL Server Profiler这个工具是SQL Trace的一个GUI的版本,而SQL Trace是一组脚本,自SQL Serv ...
- HDU 1054 Strategic Game (树形dp)
题目链接 题意: 给一颗树,用最少的点覆盖整棵树. 每一个结点可以防守相邻的一个边,求最少的点防守所有的边. 分析: 1:以当前节点为根节点,在该节点排士兵守护道路的最小消耗.在这种情况下,他的子节点 ...
- R语言实战读书笔记(四)基本数据管理
4.2 创建新变量 几个运算符: ^或**:求幂 x%%y:求余 x%/%y:整数除 4.3 变量的重编码 with(): within():可以修改数据框 4.4 变量重命名 包reshape中有个 ...
- 51nod1711 平均数
二分答案.check有多少个区间的平均数>xbi=ai-x;将sm离散化.然后logn求出有多少个小于sm[i].类似于求逆序对的思路. 一直WA一个点...所以我就下载数据特判了TAT #in ...
- org.hibernate.AnnotationException: No identifier specified for entity: cn.itcast.domain.Counter
因为我的hibernate映射表没有主键所以报这个错. 解决方案是: 1.创建一个主键 2.hibernate处理无主键的表的映射问题,其实很简单,就是把一条记录看成一个主键,即组合主键<com ...
- PHP学习笔记01——基础语法
<!DOCTYPE html> <html> <?php // 1.使用$加变量名来表示变量,php是弱类型语言,不要求在使用变量前声明,第一次赋值时变量才被创建 $a ...
- 解决Eclipse快捷键被其他软件占用
做为一个java攻城狮,eclipse是我最常用的攻城设备,eclipse快捷键 极大的提高了我的开发效率!!!! 前段时间升级了一下我的战斗装备——给电脑的系统盘换成了一个固态硬盘,因此需要重装系统 ...