WebSocket服务器与客户端的JSON服务协议

WebSocket协议允许浏览器客户端将连接视为一种"代理对等"网络。也就是说,任何客户端都可以像直接连接一样向另一个客户端发送数据——WebSocket服务器负责路由。该协议还允许多个客户端在其他人发送数据时自动接收他们感兴趣的数据——同样由WebSocket服务器进行路由。

目标是使用通用协议与服务器,而不是一些临时方案,这意味着需要更改客户端和服务器代码以添加或更改任何功能。

本文是"使用Sec-WebSocket-Protocol"的配套文章,该文章讨论了使用不同协议的WebSocket。本文描述了一个名为'JSONsvc'的协议,任何使用任何语言编写的WebSocket服务器或客户端都可以使用。服务器的示例实现是用PHP编写的,客户端代码是用HTML/javascript编写的。有关Sec-WebSocket-Protocol的使用和服务器实现的更多信息,请参见另一篇文章。

JSONsvc协议

JSONsvc允许客户端注册名为'服务'的数据,并在需要时成为'服务提供者'。每当'服务提供者'发送数据给'服务'时,JSON2svc会将该数据发送给所有注册了该服务的客户端。数据是通过使用协议:"JSONsvc"打开的WebSocket发送的。

协议处理如下数据:

{ header_json }

{ header_json }>SERVICEDATA

即一个头部'>'和一些服务数据

头部可以包含以下键值对:

  • "to":name = 目标客户端的名称或'JSONsvc'
  • "from":name = 发送者的名称 - 必需
  • "provides":service_name = 提供的服务
  • "requests":service_name = 客户端希望自动发送的服务
  • "get":service_name = 尽快请求服务
  • "put":service_name = 提供服务(可能附有>SERVICEDATA)

注意:"provides"、"requests"和"get"也可以接受服务名称的数组。

例如:

"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" }>{....任何信息....}

然后服务器会自动将整个内容转发给'Bob'。如果有多个客户端具有相同的名称或请求了相同的信息,那么他们都会收到一份副本。

嵌入的'>'字符允许服务器在不知道数据的情况下提取头部(数据可能是也可能不是JSON格式)。当数据实际上是一个几十兆字节的文件时,这一点非常重要。

由于Bob发送了"requests":["info","updates"],他将自动获得这些数据服务,每当另一个客户端提供它们时。同时,他可以通过发出"get":"servicename"进行任何数据服务的临时请求。

服务提供者

在示例中,Bob还注册为'junk'的提供者,所以如果任何其他客户端请求该服务,他可以收到...

{ "from": "some_user", "to": "Bob", "get": "junk" }

他应该用任何'junk'回复。实际上,Bob可能不是唯一连接到服务器的'junk'提供者——任何请求都只是发送给服务器在其'junk提供者'列表中找到的第一个提供者。在应用程序中,这旨在用于从两个或更多客户端应用程序共享数据(使用另一项服务)——如果一个变得不可用,另一个可以满足'junk'请求。

"put"数据格式

最初,'JSONDATA'后面的头部实际上是JSON格式的,但是,由于WebSocket能够发送任意二进制数据,因此将该数据的格式留给'服务提供者'是有用的。目前,可以这样做:

{ "from": "fred", "put": "fileservice", "filename": "somefile.xyz" }>...二进制数据...

如果要概括协议,可能希望严格定义一些更多的关键字,如上面的"filename"。(可能会想知道JavaScript客户端如何处理二进制文件——但请记住,WebSocket客户端可以是任何东西:CGI脚本、某些桌面应用程序或另一种类型的WebSocket服务器。

对于要由JavaScript使用的发送数据,可能会坚持使用JSON,只是使用类似的东西将数据与WebSocket框架分开(在这里保持简单):

JavaScript 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"的示例

一个需要在控制一组紧急响应志愿者的用户之间进行实时通信的项目,正在考虑使用带有JSONsvc的WebSocket来提供状态信息页面。在这种情况下,可以这样做:

{ "from": "viewpage", "requests": ["alerts", "pageItems"], "get": ["ItemNames", "pageItems"] }

并接收:

{ "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

这是"JSONsvc"协议的示例实现,用于WebSocket服务器。代码是用PHP编写的,就像示例服务器一样,但认为它非常直接,并且不需要很长时间就可以移植到C、java/script或其他任何东西。(例如,它花了两个晚上在C中重新实现,并嵌入到Civetweb服务器中——好吧...它几乎可以工作)

到目前为止,见过的大多数WebSocket示例只是从点到点发送字符串或一些任意的JSON编码的东西,而不是考虑伪对等的可能性。所以希望这能给一些关于如何设计和实现自己的灵活数据传输通过WebSocket的想法。

Yours, TonyWilk

历史

示例版本是1v2

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485