浏览器端JavaScript
以下是PeerFetch类中 get(url, params) 方法的几行内容:
async get ({ url = '/', params = {} }) { console.debug('PeerFetch.get enter', { url, params }) var esc = encodeURIComponent var query = Object.keys(params) .map(k => esc(k) + '=' + esc(params[k])) .join('&') url += '?' + query console.debug('PeerFetch.get', { url, query }) console.debug('PeerFetch.get post process', { url }) const request = { url, method: 'GET' } // get a ticket that matches the request // and use it to claim the corresponding // response when availably const ticket = this._enqueueRequest(request) const response = await this._receiveResponse(ticket) return response }
请注意:它可能需要在单个DataChannel上同时复用请求/响应对。这与在单个请求或响应中处理大型有效负载的问题是分开的,后者是WebRTC社区中一个仍在讨论的问题,经常使用各种非标准临时解决方案。
Edge-side(Python)
在Edge端,没有方法可以直接通过DataChannel来分别服务Web资源。但是我们可以在DataChannel上编写http代理以隐藏Web服务器(Flask)的机制。
以下是PeerFetch请求的接收端在peerjs.ext.HttpProxy中的样子:
@peerConnection.on(ConnectionEventType.Data) async def pc_data(data): log.debug('data received from remote peer \n%r', data) request = json.loads(data) # check if the request is just a keepalive ping if (request['url'].startswith('ping')): log.debug('received keepalive ping from remote peer') await _pong(peer_connection=peerConnection) return log.info('webrtc peer: http proxy request: \n%r', request) # schedule frequent pings while waiting on response_header # to keep the peer data channel open waiting_on_fetch = asyncio.Event() asyncio.create_task(_ping(peer_connection=peerConnection, stop_flag=waiting_on_fetch)) response = None try: response, content = await _fetch(**request) except Exception as e: log.exception('Error %s while fetching response' ' with request: \n %r', e, request) finally: # fetch completed, cancel pings waiting_on_fetch.set() if not response: response_header = { # internal server error code 'status': 500 } response_content = None return response_content = content response_header = { 'status': response.status, 'content-type': response.headers['content-type'], 'content-length': len(response_content) } log.info('Proxy fetched response with headers: \n%r', response.headers) log.info('Answering request: \n%r ' 'response header: \n %r', request, response_header) header_as_json = json.dumps(response_header) await peerConnection.send(header_as_json) await peerConnection.send(response_content)
请求-响应流
下列序列图是请求-响应流的简化版:
除了跟踪HTTP代理要求的请求-响应相关性之外,我们还需要处理在几秒钟不活动的情况下就意图关闭映射端口的防火墙。当HTTP代理等待Edge REST服务器响应时,它还需要保持连接机制以使DataChannel到达远程端。
强劲的外部资源
在进行此项目时,另外两个开源WebRTC项目给予了我很大帮助,那就是:
PeerJS附带了一个简单的信令服务器,该服务器非常适合该项目和简化了信令协商各个阶段的客户端。我将其拆分并为其添加了本地房间发现功能。我与PeerJS核心团队进行了讨论,以将会议室扩展引入主项目。
PeerJS没有Python版本,因此我移植了该版本。我们还进行了另外一项讨论,将Python端口包含在了PeerJS主存储库中。
Aiortc的普及反映了在以Python为主力军的IoT和AI社区中,WebRTC的实际用例数量不断增加。在Python中使用Aiortc更加容易。从Python开发人员的角度来看,该代码清晰、易于阅读和调试并做出了贡献。
总结
总的来说,能实施远程摄像机监控而不用牺牲私有数据去信任任何云服务,这是一个可行且有趣的项目。
在撰写本文时该代码版本已在我家运行了几周,也没有当过机。我期待WebRTC专家就潜在的薄弱环节和改进领域提供反馈。
文章地址:https://webrtchacks.com/private-home-surveillance-with-the-webrtc-datachannel/
原文作者:chad hart