WebSocket - 基于 Python 的主流实现方式总结

目录

一、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 一对多长连接

WebSocket - 基于 Python 的主流实现方式总结_第1张图片

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 官方文档

你可能感兴趣的:(WebSocket)