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 -网络通信协议

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 建立一对一短连接 ​

  1. # ----------- server 端 -----------
  2. import asyncio
  3. import websockets
  4. async def hello(websocket, path):
  5.    print('-------- hello opened ------')
  6.    name = await websocket.recv()
  7.    print(name)
  8.    greeting = 'hello %s' % name
  9.    await websocket.send(greeting)
  10.    print(greeting)
  11. start_server = websockets.serve(hello, 'localhost', 8765)
  12. asyncio.get_event_loop().run_until_complete(start_server)
  13. asyncio.get_event_loop().run_forever()
  14. # ----------- client 端 -----------
  15. import asyncio
  16. import websockets
  17. async def hello():
  18.    async with websockets.connect(
  19.            'ws://localhost:8765') as websocket:
  20.        name = input('your name:')
  21.        await websocket.send(name)
  22.        print(name)
  23.        greeting = await websocket.recv()
  24.        print(greeting)
  25. asyncio.get_event_loop().run_until_complete(hello())

2-0-2 建立一对一长连接

  1. # ----------- server 端 -----------
  2. import asyncio
  3. import websockets
  4. async def producer_handler(websocket, path):
  5.    print('---- 建立了连接 -----')
  6.    while True:
  7.        message = input('please input:')
  8.        await websocket.send(message)
  9. start_server = websockets.serve(producer_handler, 'localhost', 8765)
  10. asyncio.get_event_loop().run_until_complete(start_server)
  11. asyncio.get_event_loop().run_forever()
  12. # ----------- client 端 -----------
  13. import asyncio
  14. import websockets
  15. async def consumer_handler():
  16.    async with websockets.connect(
  17.            'ws://localhost:8765') as websocket:
  18.        async for message in websocket:
  19.            print(message)
  20. asyncio.get_event_loop().run_until_complete(consumer_handler())

2-0-3 建立一对多长连接

  1. # ----------- server 端 -----------
  2. import asyncio
  3. import logging
  4. import websockets
  5. logging.basicConfig()
  6. USERS = set()
  7. async def notify_users():
  8.    # 对注册列表内的客户端进行推送
  9.    if USERS:  # asyncio.wait doesn't accept an empty list
  10.        message = input('please input:')
  11.        await asyncio.wait([user.send(message) for user in USERS])
  12. async def register(websocket):
  13.    USERS.add(websocket)
  14.    await notify_users()
  15. async def unregister(websocket):
  16.    USERS.remove(websocket)
  17.    await notify_users()
  18. async def counter(websocket, path):
  19.    # register(websocket) sends user_event() to websocket
  20.    await register(websocket)
  21.    try:
  22.        # 处理客户端数据请求 (业务逻辑)
  23.        async for message in websocket:
  24.            print(message)
  25.    finally:
  26.        await unregister(websocket)
  27. asyncio.get_event_loop().run_until_complete(
  28.    websockets.serve(counter, 'localhost', 6789))
  29. asyncio.get_event_loop().run_forever()
  30. # ----------- client 端 -----------
  31. async def consumer_handler():
  32.    async with websockets.connect(
  33.            'ws://localhost:6789') as websocket:
  34.        async for message in websocket:
  35.            print(message)
  36. 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 服务端基本总结 ​

  1. import eventlet
  2. import socketio
  3. # create a Socket.IO server
  4. sio = socketio.Server()
  5. # wrap with a WSGI application
  6. app = socketio.WSGIApp(sio)
  7. # 事件处理函数的两种写法
  8. # sid:每个客户端连接的唯一标识符,一个客户端发送的所有事件具有相同的sid值
  9. @sio.event
  10. def my_event(sid, data):
  11.    pass
  12. @sio.on('my custom event')
  13. def another_event(sid, data):
  14.    pass
  15. # connetc函数 在客户端连接时自动调用 可用于验证用户身份等
  16. # environ 为字典格式,包含请求信息、http头
  17. @sio.event
  18. def connect(sid, environ):
  19.    print('connect ', sid)
  20.    # 若返回 False 则表示拒绝与客户端的联系
  21.    # return False
  22.    # 抛错,将所有参数通过拒绝消息发送给客户端
  23.    raise ConnectionRefusedError('authentication failed')
  24. # disconnect函数 在客户端断开连接时自动调用
  25. @sio.event
  26. def disconnect(sid):
  27.    print('disconnect ', sid)
  28. # socketio.Server.emit() 发送事件
  29. # sio.emit('事件名',{'具体信息数据': '……'})
  30. sio.emit('my event', {'data': 'foobar'})
  31. # room 用于标识应接受该事件的客户端sid,需要设置客户端的sid,若省略则表示广播事件
  32. sio.emit('my event', {'data': 'foobar'}, room='user_sid')
  33. # callback 回调函数,将在客户端处理事件后调用该函数,客户端返回的任何值都将作为参数给予该回调函数。
  34. # 若在广播的情况下使用回调,则服务端将有大量的调用次数
  35. sio.emit('my event', {'data': 'foobar'}, callback=my_event)
  36. # namespace 命名空间
  37. # client 为每个连接制定不同的命名空间,来打开多个连接,命名空间将作为主机名和端口后的路径名
  38. # http://example.com:8000/chat
  39. @sio.event(namespace='/chat')
  40. def my_custom_event(sid, data):
  41.    pass
  42. @sio.on('my custom event', namespace='/chat')
  43. def my_custom_event(sid, data):
  44.    pass
  45. # socketio.Namespace 基于类的命名空间
  46. # 注意:基于类的命名空间为单例,所以命名空间实例不能用于存储客户端的特定消息
  47. class MyCustomNamespace(socketio.Namespace):
  48.    # 服务器接受的任何事件,都将调用 on_ 前缀的事件名方法
  49.    # 若接受到的事件名在类内无匹配on前缀方法,则忽略。
  50.    def on_connect(self, sid, environ):
  51.        pass
  52.    def on_disconnect(self, sid):
  53.        pass
  54.    # my_event 事件触发 on_my_event 方法的执行
  55.    def on_my_event(self, sid, data):
  56.        self.emit('my_response', data)
  57. sio.register_namespace(MyCustomNamespace('/test'))
  58. # room 指定用户组
  59. # socketio.Server.enter_room() 和 socketio.Server.leave_room()方法管理其中的客户端
  60. @sio.event
  61. def begin_chat(sid):
  62.    sio.enter_room(sid, 'chat_users')
  63. @sio.event
  64. def exit_chat(sid):
  65.    sio.leave_room(sid, 'chat_users')
  66. # skip_sid 用于跳过该sid的客户端,不进行消息推送
  67. @sio.event
  68. def my_message(sid, data):
  69.    sio.emit('my reply', data, room='chat_users', skip_sid=sid)
  70. # session 用户信息存储和检索
  71. # 注意:客户端断开连接时,会破坏用户会话的内容。
  72. #       特别是,当客户端在意外断开与服务器的连接后重新连接时,不会保留用户会话内容。
  73. @sio.event
  74. def connect(sid, environ):
  75.    username = environ
  76.    # username = authenticate_user(environ)
  77.    sio.save_session(sid, {'username': username})
  78. @sio.event
  79. def message(sid, data):
  80.    session = sio.get_session(sid)
  81.    print('message from ', session['username'])
  82. # 基于上下文,管理session
  83. @sio.event
  84. def connect(sid, environ):
  85.    username = environ
  86.    # username = authenticate_user(environ)
  87.    with sio.session(sid) as session:
  88.        session['username'] = username
  89. @sio.event
  90. def message(sid, data):
  91.    with sio.session(sid) as session:
  92.        print('message from ', session['username'])

3-2-3 一对多长连接

3-2 Flask-SocketIO

Flask-SocketIO - github

  1. from flask import Flask, render_template
  2. from flask_socketio import SocketIO
  3. app = Flask(__name__)
  4. app.config['SECRET_KEY'] = 'secret!'
  5. socketio = SocketIO(app)
  6. @socketio.on('connect')
  7. def connect():
  8.    print('connection established')
  9. @socketio.on('my_event')
  10. def handle_json(json):
  11.    print('received json: ' + str(json))
  12. @socketio.on('disconnect')
  13. def test_disconnect():
  14.    print('Client disconnected')
  15. if __name__ == '__main__':
  16.    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 官方文档

(0)

相关推荐