教程:https://tutorialedge.net/python/python-socket-io-tutorial/
python-socketio 原文地址 ,在google浏览器中可以翻译为中文去使用。
首先要搞明白几个问题:
room – The recipient of the message. This can be set to the session ID of a client to address that client’s room, or to any custom room created by the application, If this argument is omitted the event is broadcasted to all connected clients.
说明
1)第一种room是每一个单独的客户端都有的。(通过session ID
可以找到)
2)第二种是应用程序自己创建的。
在下面这个方法中,如果省略掉room参数,将会自动发送给所有的连接了的客户端。
send`(*data*, *room=None*, *skip_sid=None*, *namespace=None*, *callback=None*, ***kwargs*)[¶](https://python-socketio.readthedocs.io/en/latest/#socketio.Server.send "Permalink to this definition")
译文:
Python-socketio实现了一个Python Socket.IO 服务,这个服务可以单独运行也可以综合于一个web项目中。下面是一些它的特征:
1. 对于javascript,Swift,C++,Java以及官方的Socket.IO 客户端都兼容,以及兼容任何遵循Socket.IO准则的三方客户端。
2.Python2.7, Python3.3+ 都是支持的非常好。
3.支持非常多的客户端,甚至是流行的硬件,当使用基于 [asyncio](https://docs.python.org/3/library/asyncio.html) ([sanic](http://sanic.readthedocs.io/) and [aiohttp](http://aiohttp.readthedocs.io/)), [eventlet](http://eventlet.net/) or [gevent](http://gevent.org/).
的异步服务器。在开发和测试中,任意的 `WSGI` 兼容的多线程服务器都可以使用。
4.内置了一个WSGI中间件去结合Socket.IO流量和标准WSGI应用。
5.广播消息通知所有连接的客户端,或者分配它们到“房间”。
6.多个服务器是可选支持的,通过messaging queue,比如Redis或者RabbitMQ。
7.可以通过外部的流程去发消息给客户端,比如Celery workers或者辅助的脚本。
8.基于事件的带有修饰器的架构去隐藏这个协议的一些细节。
9.支持HTTP长轮询和WebSocket传输协议。
10.支持XHR2和XHR浏览器。
11.支持文本和二进制消息。
12.支持gzip和HTTP压缩。
13.可配置的CORS响应,避免在浏览器上出现跨域问题。
什么是Socket.IO?
Socket.IO是一个基于事件的双向通讯的传输协议(一般是web浏览器),和一个服务端。原始的客户端和服务端组件实现是通过JavaScript写的。
入门指南
可以使用pip
安装Socket.IO:
pip install python-socketio
下面是一个使用aiohttp
框架(只支持Python 3.5+)实现异步IO的 Socket.IO server
简单的例子:
from aiohttp import web
import socketio
sio = socketio.AsyncServer()
app = web.Application()
sio.attach(app)
async def index(request):
"""Serve the client-side application."""
with open('index.html') as f:
return web.Response(text=f.read(), content_type='text/html')
@sio.on('connect', namespace='/chat')
def connect(sid, environ):
print("connect ", sid)
@sio.on('chat message', namespace='/chat')
async def message(sid, data):
print("message ", data)
await sio.emit('reply', room=sid)
@sio.on('disconnect', namespace='/chat')
def disconnect(sid):
print('disconnect ', sid)
app.router.add_static('/static', 'static')
app.router.add_get('/', index)
if __name__ == '__main__':
web.run_app(app)
下面是一个类似的例子,但是使用的Flask和Eventlet的例子,兼容Python2.7和3.3+:
import socketio
import eventlet
from flask import Flask, render_template
sio = socketio.Server()
app = Flask(__name__)
@app.route('/')
def index():
"""Serve the client-side application."""
return render_template('index.html')
@sio.on('connect')
def connect(sid, environ):
print('connect ', sid)
@sio.on('my message')
def message(sid, data):
print('message ', data)
@sio.on('disconnect')
def disconnect(sid):
print('disconnect ', sid)
if __name__ == '__main__':
# wrap Flask application with socketio's middleware
app = socketio.Middleware(sio, app)
# deploy as an eventlet WSGI server
eventlet.wsgi.server(eventlet.listen(('', 8000)), app)
客户端应用必须引入 socket.io-client
库(1.3.5版本以及以上,越高越好)。
每次客户端连接到服务器的连接事件处理程序调用sid(会话ID)分配给连接和WSGI环境字典。
每次客户端连接到服务端的 conenct
事件都是由sid(session ID)分配到连接和WSGI环境字典调用的。服务端可以检查身份认证或者其他的头部信息去决定是否这个客户端允许被连接。要想拒绝一个客户端的连接,这个处理器必须返回 False
。
当客户端发送发送一个事件给服务端,相应的事件处理器会被sid
和这个信息调用,可以是单个或者多个参数。这个应用可以定义尽量多的如果被需要的可以被事件处理器关联的事件。一个事件可以通过一个名称简单定义。
当一个客户端连接中断了,disconnect
事件就被调用,允许应用去执行清理工作。
服务端
Socket.IO 服务端是 socketio.Server
类的实例,他们可以被一个WSGI适用应用程序使用 socketio.Middleware
去合并:
# create a Socket.IO server
sio = socketio.Server()
# wrap WSGI application with socketio's middleware
app = socketio.Middleware(sio, app)
使用 socketio.Server.on()
方法来注册服务端的事件处理器:
@sio.on('my custom event')
def my_custom_event():
pass
对于异步服务端来说,事件处理器可以是常规方法,或者是协程:
@sio.on('my custom event')
async def my_custom_event():
await sio.emit('my reply')
聊天室
因为Socket.IO是一个双向的协议,服务端可以在任意时间发送消息给任意的连接到的客户端。为了让它方便去将客户端定位到组中,应用程序可以将客户端放入到聊天室中去,然后将消息定位到整个聊天室中。
当客户端第一次连接,他们是被分配到他们自己的聊天室中,这个聊天是是以session ID(sid 参数会传递给所有的事件处理器)命名的。应用可以通过 socketio.Server.enter_room()
和 socketio.Server.leave_room()
自由地去创建聊天室和管理客户端。客户端可以在尽量多的房间里,也可以根据需求尽量频繁地被拉入拉出聊天室。当他们的连接不在特别的时候,单独的聊天室将会分配给她它们,应用程序可以自由地增加和移除客户端从聊天室中,尽管它只要这样做就会失去定位独立客户端的能力。
@sio.on('enter room')
def enter_room(sid, data):
sio.enter_room(sid, data['room'])
@sio.on('leave room')
def leave_room(sid, data):
sio.leave_room(sid, data['room'])
socketio.Server.emit()
方法会获得一个事件名称,一个可能是 str
,bytes
,list
,dict
或者 tuple
类型的消息载体。当发送一个 tuple
,在其中的元素必须是上面的其他类型。元组中的元素将会被传递给客户端的回调函数为多个参数。定位一个个人客户端,客户端的sid
将会被给一个聊天室(假设这个应用没有修改这些初始的聊天室)。定位所有的连接的客户端们,这个聊天室参数将会被触发。
@sio.on('my message')
def message(sid, data):
print('message ', data)
sio.emit('my reply', data, room='my room')
通常在聊天室中当广播一个消息到一个用户组的时候,发送者是否接受他自己的消息是可选的。soicketio.Server.emit()
方法提供了一个可选的 skip_sid
参数去指定一个想在广播中跳过的客户端。
@sio.on('my message')
def message(sid, data):
print('message ', data)
sio.emit('my reply', data, room='my room', skip_sid=sid)
Response
当一个客户端发送一个事件给服务端,它可以选择提供一个回调方法,当服务端返回一个响应的时候会被触发。服务端可以便捷地从相应的事件处理器返回它从而提供一个响应。
@sio.on('my event', namespace='/chat')
def my_event_handler(sid, data):
# handle the message
return "OK", 123
事件处理器可以返回一个单独的值,一个带多个值的元组。这个在客户端的回调函数将会调用这些返回的值。
Callbacks
回调
服务端可以请求一个响应通过发送一个事件给客户端。socketio.Server.emit()
方法有一个可选的 callback
参数能够被设置为可回调的。当这个参数被传递之后,当客户端返回相应的时候,这个可回调的方法将会被请求。
当广播给多个客户端的时候使用回调函数是不被推荐的,因为回调方法将会被只执行一次。
Namespace
命名空间
Socket.IO 协议支持多个逻辑性连接,所有的多路复用都是在相同的物理连接上。客户端可以通过给每个连接指定不同的 namespace
从而开多个连接。一个命名空间是由 主机名+端口+路径名称构成的。比如,连接到 http://example.com:8000/chat
将会开一个连接到命名空间 /chat
。
由于分离的不同的session ID(sid
s),不同的事件处理器,不同的聊天室,每一个命名空间都是独立的。应用程序使用多个命名空间从而来区分命名空间,是非常重要的。可以参考 socketio.Server
类。
当 namespace
参数被触发了,比如设置为 None 或者 /
, 那么一个默认的命名空间将会被使用。
Class-Based Namespaces
作为一个基于装饰器的事件处理器的代替,这个属于一个命名空间事件处理器可以被创建为 socketio.Namesapce
的子类:
class MyCustomNamespace(socketio.Namespace):
def on_connect(self, sid, environ):
pass
def on_disconnect(self, sid):
pass
def on_my_event(self, sid, data):
self.emit('my_response', data)
sio.register_namespace(MyCustomNamespace('/test'))
对于基于异步io的服务端,域名空间必须继承与 socketio.AsyncNamespace
, 也可以定义普通的方法或者协程作为事件处理器:
class MyCustomNamespace(socketio.AsyncNamespace):
def on_connect(self, sid, environ):
pass
def on_disconnect(self, sid):
pass
async def on_my_event(self, sid, data):
await self.emit('my_response', data)
sio.register_namespace(MyCustomNamespace('/test'))
当使用基于类的命名空间的时候,任何被服务端接受的事件将会被分派到一个被事件名称命名的方法中作为方法名称(with the on_pfrefix
)。比如:事件 my_event
将会被一个名叫 on_my_event
的方法处理。