服务器架构
对于单个树莓派,可能会有多个观看者,但是为了操作的安全性考虑,只能有一个操作者。
而我们这个项目需要有多个树莓派,并且需要通过服务器进行信息周转,但是每个树莓派只需要接收到来自唯一操控者的消息即可。所以需要服务器进行一些过滤工作,这就需要一些权限控制。
数据结构设计
首先,每个树莓派的连接都必须进行存储。代码中,使用一个Connection类的实例对应一个树莓派的连接,并在生成的同时列入连接集合中方便查找。
同时,每个树莓派会接受许多的用户接入,为了信息广播的方便,需要有存放每个连入的用户。即整体结构为服务器下属多个树莓派,而每个树莓派包含多个用户。
其次,不能光存储连接,作为用户要使用树莓派,不仅仅是按按键而已,同时也需要进行文件的传输,而每个树莓派所下载的文件也是不一样的。所以,也需要每个树莓派的连接各自存放文件的信息。
树莓派所存储的文件可以直接写在硬盘上,使用时再通过socket或者http方式传递给树莓派,但是可能会带来安全性的问题。考虑到*.bit文件大小较小,所以决定将文件直接存放于内存中。(Update: 由于新增一些需求,最新版本将文件写在硬盘中,默认路径为/tmp/exotic/)
上述结构对应的代码如下。其中_user字段中的admin表示当前树莓派的操作者的连入句柄,只有这个句柄所属的用户发送的信息才可以被服务器转发给树莓派。
class Connection(object):
# 以list的形式存储客户端
# KeepList保证在remove的时候,不改变其余所有项目的位置
# 而是选择在原处留下删除印记,客户端的句柄会优先放置在有印记的位置
client = KeepList()
def __init__(self, stream, address):
# 存储下当前客户端的有关信息
self._stream = stream
self._address = address
self._stream.set_close_callback(self.on_close)
# 将客户端加入list并存储客户端在list中的位置
self._index = Connection.client.append(self)
# 记录所有用户的句柄,并标示出操作者
self._user = dict(
admin = None, # 操作者句柄
lock = thread.allocate_lock(), # 操作者切换时的同步锁
user = set() # 所有连入用户的集合
)
# 将上传的文件保存在内存
self._file = dict(
name = '',
body = None
)
......