目录
一、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 -网络通信协议
WebSocket 教程 - 阮一峰
Web 基于 B/S 架构,通常使用 HTTP 协议进行通信,HTTP 本质是一个单向请求,若需要持续的获取服务端的数据变化,必须轮询(polling)进行数据请求【每隔一段时间发送request,服务器将新数据返回】。
使用HTTP协议处理服务端数据监控的弊端:轮询效率低,浪费资源。因为必须不停建立连接,或保持HTTP始终连接。
wiki - 服务器推送方式
1-1 简介
为了解决上述的需求问题,并提高数据传输效率,WebSocket 协议就出现了。
WebSocket 协议下,服务端和客户端能相互的主动发送消息,建立平等对话。属于服务器推送技术的一种。
WebSocket 一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 是独立的、创建在 TCP 上的协议,和 HTTP 的唯一关联是使用 HTTP 协议的101状态码进行协议切换,使用的 TCP 端口是80,可以用于绕过大多数防火墙的限制。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端直接向客户端推送数据而不需要客户端进行请求,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并允许数据进行双向传送。
二、Websockets servers and clients in Python
websocket github
它构建于asyncio(协程)Python的标准异步I / O框架之上,提供了一个优雅的基于协程的API。
即,使用 asyncio 包裹的基本python实现方式
2-0 connect
2-0-1 建立一对一短连接
# ----------- server 端 ----------- import asyncio import websockets async def hello(websocket, path): print('-------- hello opened ------') name = await websocket.recv() print(name) greeting = 'hello %s' % name await 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 asyncio import websockets async 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 asyncio import websockets async 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 asyncio import websockets async 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 asyncio import logging import websockets logging.basicConfig() USERS = set() async def notify_users(): # 对注册列表内的客户端进行推送 if USERS: # asyncio.wait doesn't accept an empty list message = 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 websocket await 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
asyncio -- 异步 I/O
三、SocketIO
python使用websocket的几种方式
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
socket.io - 官方
3-2 python-socketio
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 eventlet import socketio # create a Socket.IO server sio = socketio.Server() # wrap with a WSGI application app = socketio.WSGIApp(sio) # 事件处理函数的两种写法 # sid:每个客户端连接的唯一标识符,一个客户端发送的所有事件具有相同的sid值 @sio.event def my_event(sid, data): pass @sio.on('my custom event') def another_event(sid, data): pass # connetc函数 在客户端连接时自动调用 可用于验证用户身份等 # environ 为字典格式,包含请求信息、http头 @sio.event def connect(sid, environ): print('connect ', sid) # 若返回 False 则表示拒绝与客户端的联系 # return False # 抛错,将所有参数通过拒绝消息发送给客户端 raise ConnectionRefusedError('authentication failed') # disconnect函数 在客户端断开连接时自动调用 @sio.event def 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): pass def 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.event def begin_chat(sid): sio.enter_room(sid, 'chat_users') @sio.event def exit_chat(sid): sio.leave_room(sid, 'chat_users') # skip_sid 用于跳过该sid的客户端,不进行消息推送 @sio.event def my_message(sid, data): sio.emit('my reply', data, room='chat_users', skip_sid=sid) # session 用户信息存储和检索 # 注意:客户端断开连接时,会破坏用户会话的内容。 # 特别是,当客户端在意外断开与服务器的连接后重新连接时,不会保留用户会话内容。 @sio.event def connect(sid, environ): username = environ # username = authenticate_user(environ) sio.save_session(sid, {'username': username}) @sio.event def message(sid, data): session = sio.get_session(sid) print('message from ', session['username']) # 基于上下文,管理session @sio.event def connect(sid, environ): username = environ # username = authenticate_user(environ) with sio.session(sid) as session: session['username'] = username @sio.event def message(sid, data): with sio.session(sid) as session: print('message from ', session['username'])
3-2-3 一对多长连接
3-2 Flask-SocketIO
Flask-SocketIO - github
from flask import Flask, render_template from flask_socketio import SocketIO app = 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 - 单纯用于连接的模块
websocket-client github
安装:Type "python setup.py install" or "pip install websocket-client" to install.
五、Tornado - 一个支持HTTP和WebSocket的Web框架
Tornado 官方文档
WebSocket - Tornado 的基本实现总结
六、Aiohttp - 基于asyncio,一个支持HTTP和WebSocket的框架
aiohttp 官方文档