JSONsvc - 一个用于 Websockets 的“代理对等/对等”协议
一种用于在 Websockets 上多个客户端之间共享数据的基于 JSON 的协议。
引言
JSONsvc 定义了一种简单的数据格式,用于在 Websocket 服务器及其客户端之间发送。它允许浏览器客户端将连接视为一种“代理点对点”网络。即,任何客户端都可以像直接连接一样向另一个客户端发送数据——websocket 服务器负责路由。
该协议还允许多个客户端在任何人发送数据时自动接收他们感兴趣的数据——同样由 websocket 服务器路由。
这里的预期兴趣在于使用带有服务器的通用协议,而不是一些临时方案,这意味着您必须更改客户端和服务器代码才能添加或更改任何功能。
背景
这是“使用 Sec-Websocket-Protocol”的配套文章,该文章讨论了与 websocket 一起使用不同协议。本文描述了一种“JSONsvc”协议,任何用任何语言编写的 websocket 服务器或客户端都可以使用该协议。服务器的示例实现恰好是 PHP,客户端代码是 HTML/javascript。有关 Sec-Websocket-Protocol 的使用和服务器实现的更多信息,请参阅另一篇文章。
JSONsvc 协议
JSONsvc 允许客户端注册命名“服务”(即数据片段),并在需要时成为“服务提供者”。每当“服务提供者”发送“服务”数据时,JSON2svc 就会将该数据发送给所有已注册的客户端。数据通过使用协议:“JSONsvc”打开的 websocket 发送
该协议处理数据如下
{ header_json }
or { header_json }>SERVICEDATA i.e. a header '>' and some service data
the header can contain the following key:value pairs:
"to":name = name of destination client or 'JSONsvc'
"from":name = name of the sender - required
"provides":service_name = service provided
"requests":service_name = service client wants sent automatically
"get":service_name = request a service as soon as possible
"put":service_name = deliver a service (could have >SERVICEDATA attached)
note:"provides","requests" and "get" can also accept an array of service names
e.g. "provides":["this_service","that_service"]
因此客户端可以发送:
{ "from":"Bob", "requests":["info","updates"], "get":"info", "provides":"junk" }
它将客户端标识为'Bob'
,并且他希望在有任何东西提供'info'
或'updates'
时接收自动更新。由于 Bob 刚刚启动,他还希望尽快"get":"info"
,他还注册为'junk'
的提供者。
如果另一个客户端'Alice'
已注册为'info'
的提供者,她将自动收到
{ "from":"Bob", "to":"Alice" "get":"info" }
她应该回复:(当然是给 websocket 服务器)
{ "from":"Alice", "to":"Bob" "put":"info" }>{.... whatever the info is ....}
服务器会自动将所有内容转发给'Bob'
。如果有多个具有相同名称或请求相同信息的客户端,那么他们都会收到一份副本。
请注意,嵌入的'>'
字符是为了允许服务器提取标头而无需了解数据(可能实际上不是 JSON 格式)。当数据实际上是数兆字节的文件时,这相当重要。
由于Bob
发送了"requests":["info","updates"]
,他将在其他客户端提供这些数据服务时自动获得它们。同时,他可以通过发出"get":"servicename"
来对任何数据服务进行临时请求。
服务提供商
在上面的例子中,Bob
也注册为"junk"
的提供者,所以如果有任何其他客户端请求该服务,他可能会收到……
{ "from":"some_user", "to":"Bob" "get":"junk" }
……他应该回复任何“垃圾”是什么。
实际上,Bob 可能不是连接到服务器的唯一“垃圾”提供者——任何请求都只是发送到服务器在其“垃圾提供者”列表中找到的第一个提供者。在我的应用程序中,这旨在从两个或更多共享数据(使用另一项服务)的客户端应用程序中传递数据——如果其中一个变得不可用,另一个可以满足“垃圾”请求。
“put”数据格式
最初的目的是让标头后面的“JSONDATA”实际采用 JSON 格式,但是,由于 websocket 能够发送任意二进制数据,因此将该数据的格式留给“服务提供者”是有用的。
目前你可以这样做
{"from":"fred", "put":"fileservice", "filename":"somefile.xyz" }>... binary data...
如果你要泛化协议,你可能希望严格定义更多的关键字,比如上面的"filename"
。(你可能会想一个 javascript 客户端如何处理二进制文件——但请记住,websocket 客户端可以是任何东西:一个 cgi 脚本、一些桌面应用程序或另一种类型的 websocket 服务器)
为了发送供 javascript 使用的数据,你可能会坚持使用 JSON,只需使用类似(这里保持简单)的东西将数据与 websocket 帧分开。
host="ws://some_server/";
socket= new WebSocket( host, "JSONsvc");
socket.onopen = function(msg){
socket.send( '{"from":"me", "requests":["alert","UserList"]}' );
}
socket.onmessage = function (msg) {
var separatorIndex=msg.data.indexOf('>');
if( separatorIndex > 0 )
header= JSON.parse( msg.data.slice(0,separatorIndex) );
data= JSON.parse( msg.data.slice(separatorIndex+1) );
}else{
header= JSON.parse( msg.data );
data= someDefaultThing;
}
if( header.put == "alertService" ) alert( data.msg );
else if( header.put =="UserList" ) updateUserList( data );
else ...
}
websocket2.html 示例通过实现“聊天”服务器以一种简单的方式演示了 JSONsvc。使用该示例;启动 JSONsvc 协议并输入一条消息——显示结果数据。然后点击“Log decoded”并打开多个浏览器与自己聊天 (!),每个客户端都会自动更新消息和当前客户端列表。
在 websocket2.html 中,JSONsvc 请求通过以下方式形成
request= {from:"Anonymous", requests:["text","JSONsvc_ClientList"], put:"text" }; if( username != "" ) request.from= username_from_textbox; msg= JSON.stringify( request ) + ">" + message_from_textbox;
发送请求结果
Send: {"from":"Bob","requests":["text","JSONsvc_ClientList"],"put":"text"}>Hello there Recv: {"from":"Bob","put":"text","to":"Bob"}>Hello there Recv: {"from":"JSONsvc","put":"JSONsvc_ClientList","to":"Bob"}>["Alice","Bob","Carol"]
在这里我们收到了两条消息,第一条是我们发送的(因为我们提供了并请求了"text"
服务),第二条是因为我们请求了 "JSONsvc_ClientList"
,它在客户端列表更改时发送连接客户端的列表。如果另一个用户发送消息,我们可能会收到
Recv: {"from":"Carol","put":"text","to":"Bob"}>hi from Carol
一个更“restful”的例子
一个项目需要控制一组应急响应志愿者的用户之间进行实时通信,该项目正在考虑使用 Websockets 和 JSONsvc 来提供状态信息页面。在这种情况下,我们可以做这样的事情
// on entry to a 'view' page we send: { "from","viewpage", "requests":["alerts","pageItems"], "get":["ItemNames","pageItems"]} // and receive: { "from","JSONsvc", "to","viewpage", "put":"ItemNames"}>{"name":40,"status":100} { "from","JSONsvc", "to","viewpage", "put":"pageItems"}> {"name":"Fred Bloggs", "status","off duty"}
javascript 使用"pageItemNames"
为该项目列表构建页面视图,并在收到"pageItems"
时填充数据。这样,javascript 不需要知道中央数据库的任何内容,并且每当有东西发送更新版本的"pageItems"
时,它都会刷新页面。此示例还注册了"alerts"
,可用于紧急“弹出”消息。(实际上,还添加了一些标签来标识页面、提供屏幕图例、标记可编辑项目等)
JSONsvc2.php
这是 Websocket 服务器的“JSONsvc”协议的示例实现。
该代码恰好是用 PHP 编写的,就像示例服务器一样,但我认为它非常简单,移植到 C、Java/脚本或任何其他语言不需要很长时间。(例如,在 C 中重新实现它并将其嵌入到Civetweb服务器中花了几个晚上——嗯……它几乎可以工作了)
最后
我目前看到的大多数 websocket 示例只是从点对点发送字符串或一些任意的 JSON 编码数据,而不是考虑伪点对点的可能性。所以我希望这能给你一些关于如何设计和实现你自己的灵活数据传输通过 websockets 的想法。你的,
TonyWilk
历史
示例版本为 1v2
截至 2014 年 1 月 1 日,浏览器原生支持 websockets v13
PC:Internet Explorer 11、Chrome 32、Firefox 26.0 和 Android:Chrome 31、Opera 18
在 Windows PC/笔记本电脑、三星 S3/4、亚马逊 Fire 和 Nexus 7 上