WebSocket
目录
一、WebSocket -网络通信协议
1-1 简介
二、Websockets servers and clients in Python
2-0 connect
2-0-1 建立一对一短连接
2-0-2 建立一对一长连接
2-0-3 建立一对多长连接
2-1 asyncio
三、SocketIO
3-0 Flask-Sockets VS Flask-SocketIO
3-1 Socket.IO
3-2 python-socketio
3-2-0 安装
3-2-1 服务端基本总结
3-2-3 一对多长连接
3-2 Flask-SocketIO
四、WebSocket for client - 单纯用于连接的模块
五、Tornado - 一个支持HTTP和WebSocket的Web框架
六、Aiohttp - 基于asyncio,一个支持HTTP和WebSocket的框架
一、WebSocket -网络通信协议
Web 基于 B/S 架构,通常使用 HTTP 协议进行通信,HTTP 本质是一个单向请求,若需要持续的获取服务端的数据变化,必须轮询(polling)进行数据请求【每隔一段时间发送request,服务器将新数据返回】。
使用HTTP协议处理服务端数据监控的弊端:轮询效率低,浪费资源。因为必须不停建立连接,或保持HTTP始终连接。
1-1 简介
为了解决上述的需求问题,并提高数据传输效率,WebSocket 协议就出现了。
WebSocket 协议下,服务端和客户端能相互的主动发送消息,建立平等对话。属于服务器推送技术的一种。
WebSocket 一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 是独立的、创建在 TCP 上的协议,和 HTTP 的唯一关联是使用 HTTP 协议的101状态码进行协议切换,使用的 TCP 端口是80,可以用于绕过大多数防火墙的限制。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并允许数据进行双向传送。
二、Websockets servers and clients in Python
它构建于asyncio(协程)Python的标准异步I / O框架之上,提供了一个优雅的基于协程的API。
即,使用 asyncio 包裹的基本python实现方式
2-0 connect
2-0-1 建立一对一短连接
# ----------- server 端 -----------import asyncioimport websocketsasync def hello(websocket, path):print('-------- hello opened ------')name = await websocket.recv()print(name)greeting = 'hello %s' % nameawait websocket.send(greeting)print(greeting)start_server = websockets.serve(hello, 'localhost', 8765)asyncio.get_event_loop().run_until_complete(start_server)asyncio.get_event_loop().run_forever()# ----------- client 端 -----------import asyncioimport websocketsasync def hello():async with websockets.connect('ws://localhost:8765') as websocket:name = input('your name:')await websocket.send(name)print(name)greeting = await websocket.recv()print(greeting)asyncio.get_event_loop().run_until_complete(hello())
2-0-2 建立一对一长连接
# ----------- server 端 -----------import asyncioimport websocketsasync def producer_handler(websocket, path):print('---- 建立了连接 -----')while True:message = input('please input:')await websocket.send(message)start_server = websockets.serve(producer_handler, 'localhost', 8765)asyncio.get_event_loop().run_until_complete(start_server)asyncio.get_event_loop().run_forever()# ----------- client 端 -----------import asyncioimport websocketsasync def consumer_handler():async with websockets.connect('ws://localhost:8765') as websocket:async for message in websocket:print(message)asyncio.get_event_loop().run_until_complete(consumer_handler())
2-0-3 建立一对多长连接
# ----------- server 端 -----------import asyncioimport loggingimport websocketslogging.basicConfig()USERS = set()async def notify_users():# 对注册列表内的客户端进行推送if USERS: # asyncio.wait doesn't accept an empty listmessage = input('please input:')await asyncio.wait([user.send(message) for user in USERS])async def register(websocket):USERS.add(websocket)await notify_users()async def unregister(websocket):USERS.remove(websocket)await notify_users()async def counter(websocket, path):# register(websocket) sends user_event() to websocketawait register(websocket)try:# 处理客户端数据请求 (业务逻辑)async for message in websocket:print(message)finally:await unregister(websocket)asyncio.get_event_loop().run_until_complete(websockets.serve(counter, 'localhost', 6789))asyncio.get_event_loop().run_forever()# ----------- client 端 -----------async def consumer_handler():async with websockets.connect('ws://localhost:6789') as websocket:async for message in websocket:print(message)asyncio.get_event_loop().run_until_complete(consumer_handler())
2-1 asyncio
三、SocketIO
3-0 Flask-Sockets VS Flask-SocketIO
Flask-Sockets
Flask-Sockets 只是实现通信通道,发送的是完全取决于应用程序。Flask-Sockets 仅仅将WebSocket协议(通过使用gevent-websocket项目)进行包装,因此它 只适用于原生支持WebSocket协议的浏览器,对于那些不支持WebSocket协议的较老的浏览器,无法使用Flask-SocketIO
Flask-SocketIO不仅实现了WebSocket协议,并且对于那些不支持WebSocket协议的旧版浏览器,使用它也能够实现相同的效果。新版旧版的浏览器都能使用他 。Flask-SocketIO 实现了SocketIO Javascript库公开的消息传递协议。Flask-SocketIO 还为事件处理程序创建了一个类似flask的常规视图函数的环境,包括创建应用程序和请求上下文。 然而,在文档中会介绍一些重要的例外情形。
3-1 Socket.IO
3-2 python-socketio
3-2-0 安装
服务端安装:pip install python-socketio客户端安装:pip install "python-socketio[client]"
安装问题 :module 'importlib._bootstrap' has no attribute 'SourceFileLoader'
错误分析:setuptools版本过久,需要更新
解决方式:python -m ensurepip --upgrade
3-2-1 服务端基本总结
import eventletimport socketio# create a Socket.IO serversio = socketio.Server()# wrap with a WSGI applicationapp = socketio.WSGIApp(sio)# 事件处理函数的两种写法# sid:每个客户端连接的唯一标识符,一个客户端发送的所有事件具有相同的sid值@sio.eventdef my_event(sid, data):pass@sio.on('my custom event')def another_event(sid, data):pass# connetc函数 在客户端连接时自动调用 可用于验证用户身份等# environ 为字典格式,包含请求信息、http头@sio.eventdef connect(sid, environ):print('connect ', sid)# 若返回 False 则表示拒绝与客户端的联系# return False# 抛错,将所有参数通过拒绝消息发送给客户端raise ConnectionRefusedError('authentication failed')# disconnect函数 在客户端断开连接时自动调用@sio.eventdef disconnect(sid):print('disconnect ', sid)# socketio.Server.emit() 发送事件# sio.emit('事件名',{'具体信息数据': '……'})sio.emit('my event', {'data': 'foobar'})# room 用于标识应接受该事件的客户端sid,需要设置客户端的sid,若省略则表示广播事件sio.emit('my event', {'data': 'foobar'}, room='user_sid')# callback 回调函数,将在客户端处理事件后调用该函数,客户端返回的任何值都将作为参数给予该回调函数。# 若在广播的情况下使用回调,则服务端将有大量的调用次数sio.emit('my event', {'data': 'foobar'}, callback=my_event)# namespace 命名空间# client 为每个连接制定不同的命名空间,来打开多个连接,命名空间将作为主机名和端口后的路径名# http://example.com:8000/chat@sio.event(namespace='/chat')def my_custom_event(sid, data):pass@sio.on('my custom event', namespace='/chat')def my_custom_event(sid, data):pass# socketio.Namespace 基于类的命名空间# 注意:基于类的命名空间为单例,所以命名空间实例不能用于存储客户端的特定消息class MyCustomNamespace(socketio.Namespace):# 服务器接受的任何事件,都将调用 on_ 前缀的事件名方法# 若接受到的事件名在类内无匹配on前缀方法,则忽略。def on_connect(self, sid, environ):passdef on_disconnect(self, sid):pass# my_event 事件触发 on_my_event 方法的执行def on_my_event(self, sid, data):self.emit('my_response', data)sio.register_namespace(MyCustomNamespace('/test'))# room 指定用户组# socketio.Server.enter_room() 和 socketio.Server.leave_room()方法管理其中的客户端@sio.eventdef begin_chat(sid):sio.enter_room(sid, 'chat_users')@sio.eventdef exit_chat(sid):sio.leave_room(sid, 'chat_users')# skip_sid 用于跳过该sid的客户端,不进行消息推送@sio.eventdef my_message(sid, data):sio.emit('my reply', data, room='chat_users', skip_sid=sid)# session 用户信息存储和检索# 注意:客户端断开连接时,会破坏用户会话的内容。# 特别是,当客户端在意外断开与服务器的连接后重新连接时,不会保留用户会话内容。@sio.eventdef connect(sid, environ):username = environ# username = authenticate_user(environ)sio.save_session(sid, {'username': username})@sio.eventdef message(sid, data):session = sio.get_session(sid)print('message from ', session['username'])# 基于上下文,管理session@sio.eventdef connect(sid, environ):username = environ# username = authenticate_user(environ)with sio.session(sid) as session:session['username'] = username@sio.eventdef message(sid, data):with sio.session(sid) as session:print('message from ', session['username'])
3-2-3 一对多长连接
3-2 Flask-SocketIO
from flask import Flask, render_templatefrom flask_socketio import SocketIOapp = Flask(__name__)app.config['SECRET_KEY'] = 'secret!'socketio = SocketIO(app)@socketio.on('connect')def connect():print('connection established')@socketio.on('my_event')def handle_json(json):print('received json: ' + str(json))@socketio.on('disconnect')def test_disconnect():print('Client disconnected')if __name__ == '__main__':socketio.run(app)
四、WebSocket for client - 单纯用于连接的模块
安装:Type "python setup.py install" or "pip install websocket-client" to install.
五、Tornado - 一个支持HTTP和WebSocket的Web框架
六、Aiohttp - 基于asyncio,一个支持HTTP和WebSocket的框架

