本文来源:https://www.jianshu.com/p/d81397edd2b1

websocket是html5中实现了服务端和客户端进行双向文本或二进制数据通信的一种新协议,其实已经低于HTTP协议本身和HTTP本质上没有什么关系了。不过形式上两者还是有想象之处。因此websocket的连接地址是长这样的:ws://localhost:8080。可以看到,协议修饰符不是http了。

  另外,websocket在连接建立阶段是通过HTTP的握手方式进行的,这可以看做是为了兼容浏览器或者使用一些现成的功能来实现,这样一种捷径。当连接建立之后,客户端和服务端之间就不再进行HTTP通信了,所有信息交互都由websocket接管。

  从资源占用的角度上来说,其实websocket比ajax占用的资源更多,但它真正实现了全双工通信这一点还是很理想的,意味着无论是前端还是后台的信息交互程序编写都会变得更加方便。由于采用了新的协议,所以我们也需要适当地改造下前后台的程序

对app进行一些路由设置
对socketio进行一些监听设置
socketio的监听设置,这才是真正关系到前后端websocket通信过程的。
@socketio.on('request_for_response',namespace='/testnamespace')
def give_response(data):
value = data.get('param') #进行一些对value的处理或者其他操作,在此期间可以随时会调用emit方法向前台发送消息
emit('response',{'code':'200','msg':'start to process...'}) time.sleep(5)
emit('response',{'code':'200','msg':'processed'})

socketio也用了和app.route类似的装饰器的形式进行监听设置。主要参数中有namespace这一项,也就是这项指定了这个监听的范围。

在前端,只有注册在testnamespace上的socket,emit向request_for_response的消息才会被这个函数接受并处理。

处理函数自带一个参数用来接收前端emit来消息中的那个object,在处理函数中可以对其解析处理。随后后端向前端发送了start to process的消息。也使用了emit这个方法,然后指明了监听是response。也就是说前端on在response上的监听处理函数会处理这个消息(当然还是在testnamespace的框架内)。发出消息后后端不会被阻塞而是继续向下执行,在处理了5秒钟之后发出了结束处理的消息,前端自然隔了五秒之后就得到了这个消息了。

socket监听响应函数本身不需要返回什么值,只需要在处理过程中适当的位置emit出消息即可。

关于send和emit方法:

网上其他一些教程中会提到send方法来取代emit方法的位置(无论是前端还是后端),其实send方法就是把上文中的'request_for_response','response'这两个标识都默认成'message'。如此在写的时候就不用写事件名,直接写要传递的参数即可。反过来看,用emit方法实际上是做了一个自定义事件的工作,可以说更加灵活多变一点

flask-socketio

为flask应用提供了一个客户端与服务器之间低延迟的双向通信。客户端应用可以用Javascript,C++,Java,Swift或者其它任意的编程语言的socketio官方库的客户端去和服务端创建一个永久的连接。

1.flask-socketio的依赖

这个异步的服务的包的依赖可以有三个选择:

  • eventlet:这是最好的选择,支持长连接(long-polling)和websocket传输。

  • gevent: 支持许多不同的配置,长连接传输是完全支持的,但是不同于eventlet,gevent并没有原生支持websocket。添加websocket(功能)有两种方法:gevent-websocket包为gevent添加了websocket支持,但是不幸的是,这个包只能用于python2;至于另外一个选择,是用uWSGI网络服务器,这个能够在功能上支持websocket。gevent依然是可操作的选择,但是优先级略微地低于eventlet。

  • 基于Werkzeug开发的flask服务器也是可行的,使用缺乏可操作性的caveat,它仅可以被用于简化workflow的开发。这个方案仅支持长连接方式传输。

这个扩展自动寻找已安装的异步框架来使用。最优先的是eventlet,其次是gevent。在gevent中,对于websocket的支持,uWSGI是优先考虑的,其次是gevent-websocket。如果eventlet和gevent都没有被安装,那么就使用flask-development将会被启用。

如果使用多进程,一个消息队列服务将会被进程用来协调操作,例如广播。支持这个队列的有Redis,RabbitMQ,还有其他由Kombu支持的包。

在客户端,Javascript官方的SOcket.IO可以用来创建一个与服务端通信的连接。这里有许多用Swift,Java,C++编写的官方客户端。非官方的客户端也是可以工作的,只要他们支持了Socket.IO协议。

2.Flask-SocketIO引入到Flask应用

from flask import Flask, render_template
from flask_socketio import SocketIO

app = Flask(__name__)
app.configp['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

if __name__ == '__main__':
  socketio.run(app)

init_app()风格的初始化也是支持的。

注意网络服务器的启动。

函数socketio.run()封装了网络服务器的启动部分,并且代替了flask开发服务器的标准启动语句app.run()。当应用在debug模式下,Werkzeug开发服务器也是在socketio.run()中被合理地应用和配置。如果可用的话,在生产模式下eventlet网络服务器也是被应用的,否则,gevent网络服务器将会被启用。如果eventlet和gevent都没有被安装,那么将会使用Werkzeug开发网络服务器。

在flask 0.11中被引入的可点击命令行界面也是被支持的。这个扩展提供了一个新版的flask run命令,适合启动一个Socket.IO服务器。用法示例:

FLASK_APP = my_app flask run

这个应用只能为那种连接到客户端的页面服务,并且客户端还需引用Socket.IO库并且建立一个连接:

<script type="text/javascript" scr="//cdn.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>

<script type="text/javascript" charset="utf-8">
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
</script>

3.如何接收消息

在使用SocketIO的时候,消息将被作为活动(event)的两端接收。

在客户端使用JavaScript回叫信号。

使用Flask-SocketIO服务器,需要为这些活动注册处理器(handler),类似于视图函数怎样处理路由。

4.未命名的活动

创建了一个服务端的活动处理器(event handler):使用了字符串消息/json。

@socketio.on('message')
def handle_message(message):
  print('received message: ' + message)

@socketio.on('json')
def handle_json(json):
  print('received json: '+ str(json))

5.自定义的活动名称

这些活动的消息数据类型可以是字符串,字节,整型,或者JSON:

@socketio.on('my event')
def handle_my_custom_event(json):
  print('received json: ' + str(json))
自定义名称的活动可以支持多参数:

@socketio.on('my event')
def handle_my_sustom_event(arg1, arg2, arg3):

  print('received args: ' + arg1 + arg2 + arg3)

命名活动是极度复杂的,在其消除了额外的元数据(metadata)来描述消息类型的时候。

6.命名空间

Flask-SocketIO同样支持命名空间(namespace),这个功能允许客户端在一个相同的物理socket上多路复用几个独立的连接:

@scoketio.on('my event', namespace='/test')
def handle_my_custom_namespace_event(json):
  print('received json: ' + str(json))
当一个命名空间没有具体指出,一个全局的命名空间'/'将会被启用

有时,装饰器的语法并不方便,on_event()方法可以作为替代

def my_function_handler(data):
  pass
socketio.on_event('my event', my_function_handler, namespace='/test')

7.返回值

客户端要求一个确认回复,来确认消息的接收。任何一个从处理函数(handler function)中返回的值都会在回调函数中作为一个参数返回给客户端。

@socketio.on('my event')
def handle_my_custom_event(json):
  print('received json: ' + str(json))
  return('one', 2)
在上面的例子中,客户端回调函数将会回调两个参数,one和2。如果处理函数没有返回值,这个客户端回调函数将以没有参数的情况返回。

8.发送消息

之前章节定义的SocketIO活动处理函数,可以凭借send()函数和emit()函数来主动连接客户端。

9.未命名的活动

from flask_socketio import send, emit

@socketio.on('message')
def handle_message(message):
  send(message)

@socketio.on('json')
def handle_json(json):
  send(json, json=True)

@socket.on('my event')
def handle_my_custom_event(json):
  emit('my response', json)

有命名空间的活动

send()和emit()默认用在接下来的消息中。不同的命名空间可以被具体化到可选择的可选择的命名空间参数上

@socketio.on('message')
def handle_message(message):
  send(message, namespace='/chat')

@socketio.on('message')
def handle_my_sustom_event(json):
  emit('my response', json, namespace='/chat')
为了实现发送一个多参数的活动,发送一个元组:

使用回调时,JavaScript客户端使用回调函数在接收到的信息时回调。在客户端应用启用回调函数时,服务器会启用服务端相匹配的函数去响应。如果客户端没有回调任何值,这些将会作为服务端的响应被提供。

也就是说无论客户端是否接收到,服务端一定会执行回调函数

def ack():
  print('message was received!')

@socketio.on('my event')
def handle_my_custom_event(json):
  emit('my response', json, callback=ack)

客户端的应用同样要求一个来自服务端的确认信息。如果服务端想为一次响应提供一个参数,它必须要在活动处理函数中被返回。

@socketio.on('my event')
def handle_my_custom_event(json):
  # ... handle the event
  return 'foo', 'bar', 123 # client callback will receive these 3 arguments

10.广播

SocketIO另外一个非常有用的特性就是广播消息。Flask-SocketIO中,只要将broadcast = True这个可选参数加到send()和emit()中即可:

@socketio.on('my event')
def handle_my_custom_event(data):
  emit('my response', data, broadcast=True)
当一个消息以广播选项被开启的情况下被发出的时候,连接到这个命名空间的所有客户端都会收到这个消息。注意:广播的消息将不会被回调。

所有的例子表明,直到这个节点服务器才回复客户端发出的这个活动。但是另外的应用中,服务器需要成为消息的发起者。对于起源于服务器的活动而言,这个有利于发送通知到客户端,比如在后台线程中。socketio.send()和socketio.emit()方法可以用来对所有的连接进行广播。

def some_function():
  socketio.emit('some event', {'data': 42})

注意:通过对send()和emit()的上下文的感知,socketio.send()和socketio.emit()不是相同的函数。同样需要注意的是:以上的用法是没有客户端内容,所以假定broadcast=True,并且需要被具体化。

flask-SocketIO的更多相关文章

  1. flask+socketio+echarts3 服务器监控程序(基于后端数据推送)

    本文地址:http://www.cnblogs.com/hhh5460/p/7397006.html 说明 以前的那个例子的思路是后端监控数据存入数据库:前端ajax定时查询数据库. 这几天在看web ...

  2. flask socketio 踩坑记录

    在使用python3的flask-socketio+socket.io.js的时候报错 在使用python3的flask-socketio+socket.io.js的时候报错"unsuppo ...

  3. cozmo 入坑日记及开发环境搭建

    前几日,朋友在群里发了一个机器人的小视频,视频里机器人可以对话,可以推箱子,开心以后会哈哈大笑,非常有趣. 详细了解里一下,这是个叫 cozmo 的智能机器人,可以配合 SDK 用 python 编程 ...

  4. websocket理解

    简介 在实际开发中,可能会出现一个需求场景,要求网页的数据可以实时更新.在这种情况下,我们一般会采用轮询的方式,间隔性获取数据,即通过定时器间隔性请求相应接口获取数据,此方式由于是不断请求服务器,资源 ...

  5. 【Flask】 flask-socketio实现WebSocket

    [flask-socektio] 之前不知道在哪个场合下提到过如何从web后台向前台推送消息.听闻了反向ajax技术这种模式之后,大呼神奇,试了一下之后发现也确实可以用.不过,反向ajax的代价也很明 ...

  6. Flask速成项目:Flask实现计算机资源的实时监控

    很多人都说使用Python开发WEB应用非常方便,那么对于WEB新手来说,到底有多方便呢?本文即将展示给你Python的魔法. 本文将通过一个实例:Flask实现计算机资源的实时监控,迅速带你入门Fl ...

  7. 结合manage.py,在flask项目中使用websocket模块--- flask-socketio

    前言:       - 为什么我要使用 flask-socketio模块,而不是flask-sockets?       - 因为flask-socketio与前端流行的websocket库socke ...

  8. python中socket、socketio、flask-socketio、WebSocket的区别与联系

    socket.socketio.flask-socketio.WebSocket的区别与联系 socket 是通信的基础,并不是一个协议,Socket是应用层与TCP/IP协议族通信的中间软件抽象层, ...

  9. 基于flask的网页聊天室(四)

    基于flask的网页聊天室(四) 前言 接前天的内容,今天完成了消息的处理 具体内容 上次使用了flask_login做用户登录,但是直接访问login_requare装饰的函数会报401错误,这里可 ...

  10. 【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)

    问题描述 使用 python websockets 模块作为Socket的服务端,发布到App Service for Linux环境后,发现Docker Container无法启动.错误消息为: 2 ...

随机推荐

  1. 【持久化框架】SpringMVC+Spring4+Mybatis3集成,开发简单Web项目+源码下载

    上篇博文我们介绍了mybatis的基本概念与原理,这篇博文我们通过Spring与Mybatis集成,开发一个简单用户增删改查的Web项目. 基本准备工作 1.安装JDK1.6以上版本,安装与配置 2. ...

  2. java 多线程系列---JUC原子类(四)之AtomicReference原子类

    AtomicReference介绍和函数列表 AtomicReference是作用是对"对象"进行原子操作. AtomicReference函数列表   // 使用 null 初始 ...

  3. 程序中使用log4J打印信息的两种方式

    (1)通过org.apache.commons.logging.Log 接口实例化: public static Log log = LogFactory.getLog(String name); p ...

  4. 地图投影的N种姿势(转载)

    转载地址:http://blog.sina.com.cn/s/blog_517eed9f0102w4rm.html 一篇题为<我们看到的地图一直都错得离谱……>的文章在朋友圈里莫名流行起来 ...

  5. IE双边距bug

    标准参考 根据 W3C CSS2.1 规范中的描述,对于非替换的浮动元素,若 'margin-left' 或 'margin-right' 特性的计算值为 'auto',则它们的实际使用值为 '0'. ...

  6. js中FOR循环的陷阱

    //闭包解决 循环输出的问题 for(var i=0;i<rows.length;i++) {( function (i) { })(i);

  7. HDU 4348(主席树 标记永久化)

    题面一看就是裸的数据结构题,而且一看就知道是主席树... 一共四种操作:1:把区间[l, r]的数都加上d,并且更新时间.2:查询当前时间的区间和.3:查询历史时间的区间和.4:时光倒流到某个时间. ...

  8. CURD 操作 [1]

    create创建新数据 首先在主目录下创建index.html,与index.php同级,插入以下代码 <meta charset="utf-8"> <form ...

  9. 报错:空指针java.lang.NullPointerException 原因 Action层 private UserService userservice 上未加@Autowire注解

    java.lang.NullPointerException at com.itheima.test.Test2.fun1(Test2.java:18) at sun.reflect.NativeMe ...

  10. java8的十大新特性

    推荐学习的博客: http://blog.csdn.net/renfufei/article/details/24600507/-------讲解的非常通俗易懂 http://blog.csdn.ne ...