socketio如何过滤事件(特别是flask-socketio)
How does socketio filter events (specifically flask-socketio)
背景
假设有 N 个客户端和 N 个节点,每个节点都会发出一条消息,其中包含统计信息和它的名称。单个客户端 n ∈ N 只关心特定节点 n ∈ N.
建议的解决方案
当服务器收到来自节点的消息时,每个客户端都会监听一个特定的事件 node_namenode_name, 发出一个事件 node_name
问题
事件发出时node_name,
是否会将消息发送给每个客户端 n ∈ N,然后在没有针对特定 node_name
的侦听器时被丢弃
或
服务器是否保留元数据并知道哪些 clients/connections 正在侦听事件 node_name 并且只发送给特定的客户端 n
修改
后者能否通过命名空间实现
或
是否建议为每个节点创建一个房间?这些房间会充当元数据
事件在客户端级别过滤,而命名空间和房间在服务器级别
同一命名空间和房间内的所有已连接客户端都会收到所有事件
查看源代码,我们发现 flask_socketio 在 socketio 之上提供了一个层,其中包含一些包装函数 __init__.py
从其核心文件的实例化属性调用函数。
base_manager.py
中的 emit
函数本身就是
def emit(self, event, data, namespace, room=None, skip_sid=None,
callback=None, **kwargs):
"""Emit a message to a single client, a room, or all the clients
connected to the namespace."""
if namespace not in self.rooms or room not in self.rooms[namespace]:
return
for sid in self.get_participants(namespace, room):
if sid != skip_sid:
if callback is not None:
id = self._generate_ack_id(sid, namespace, callback)
else:
id = None
self.server._emit_internal(sid, event, data, namespace, id)
它使用了一个名为 get_participants
的函数,看起来像这样
def get_participants(self, namespace, room):
"""Return an iterable with the active participants in a room."""
for sid, active in six.iteritems(self.rooms[namespace][room].copy()):
yield sid
最后看一下函数 enter_room
揭示了存储客户端层次结构的对象的结构
def enter_room(self, sid, namespace, room):
"""Add a client to a room."""
if namespace not in self.rooms:
self.rooms[namespace] = {}
if room not in self.rooms[namespace]:
self.rooms[namespace][room] = {}
self.rooms[namespace][room][sid] = True
鉴于结构,最好使用node_name作为特定预定义命名空间下的房间名称。
背景
假设有 N 个客户端和 N 个节点,每个节点都会发出一条消息,其中包含统计信息和它的名称。单个客户端 n ∈ N 只关心特定节点 n ∈ N.
建议的解决方案
当服务器收到来自节点的消息时,每个客户端都会监听一个特定的事件 node_namenode_name, 发出一个事件 node_name
问题
事件发出时node_name,
是否会将消息发送给每个客户端 n ∈ N,然后在没有针对特定 node_name
的侦听器时被丢弃或
服务器是否保留元数据并知道哪些 clients/connections 正在侦听事件 node_name 并且只发送给特定的客户端 n
修改
后者能否通过命名空间实现
或
是否建议为每个节点创建一个房间?这些房间会充当元数据
事件在客户端级别过滤,而命名空间和房间在服务器级别
同一命名空间和房间内的所有已连接客户端都会收到所有事件
查看源代码,我们发现 flask_socketio 在 socketio 之上提供了一个层,其中包含一些包装函数 __init__.py
从其核心文件的实例化属性调用函数。
base_manager.py
中的 emit
函数本身就是
def emit(self, event, data, namespace, room=None, skip_sid=None,
callback=None, **kwargs):
"""Emit a message to a single client, a room, or all the clients
connected to the namespace."""
if namespace not in self.rooms or room not in self.rooms[namespace]:
return
for sid in self.get_participants(namespace, room):
if sid != skip_sid:
if callback is not None:
id = self._generate_ack_id(sid, namespace, callback)
else:
id = None
self.server._emit_internal(sid, event, data, namespace, id)
它使用了一个名为 get_participants
的函数,看起来像这样
def get_participants(self, namespace, room):
"""Return an iterable with the active participants in a room."""
for sid, active in six.iteritems(self.rooms[namespace][room].copy()):
yield sid
最后看一下函数 enter_room
揭示了存储客户端层次结构的对象的结构
def enter_room(self, sid, namespace, room):
"""Add a client to a room."""
if namespace not in self.rooms:
self.rooms[namespace] = {}
if room not in self.rooms[namespace]:
self.rooms[namespace][room] = {}
self.rooms[namespace][room][sid] = True
鉴于结构,最好使用node_name作为特定预定义命名空间下的房间名称。