我正在编写一个简单的流式JSON服务。它由长时间(数周或数月(间歇性发送的JSON消息组成。
在纯TCP套接字上发送多个JSON消息的最佳实践是什么?
我研究过的一些替代方案(及其缺点(是:
- 换行符分隔的JSON-缺点:JSON中的换行符需要转义或禁止
- websocket启发的0x000xff帧-缺点:它现在是二进制的,不再是utf-8
- 真正的websockets-缺点:缺少(开源(websocket客户端库
- http多部分http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html-缺点:客户支持不完整
- 没有分隔符-缺点:分块需要JSON解析(因为字符串中的curlies,所以不能只计算curlies(
有没有一种好的,或者至少是行之有效的方法来做到这一点?
我的前两个选项是:
-
做早期TCP协议所做的事情:发送一条消息(在您的情况下是JSON对象(并关闭连接。客户端检测到它并重新打开以获取下一个对象。
- 优点:非常容易解析,无需发送额外的(内容(字节。任何数据丢失都意味着只丢失一个对象。如果你能忍受,就没有必要在你的应用程序中添加转发
- 缺点:如果发送(大量((非常(小的对象,三包TCP握手会增加延迟
-
执行分块模式HTTP的操作:首先发送JSON对象中的字节数、换行符(HTTP中的CRLF(和JSON对象。客户端只需要计算字节数就可以知道下一个字节何时是下一个对象大小。
- 优点:你保留了一个长期流
- 缺点:额外的几个字节,你必须保持一个长期的流,所以意外中断和重新连接必须作为异常事件处理,需要建立一些握手才能在失败的地方继续
当您想要为浏览器客户端提供服务时,最接近原始TCP的是WebSockets。
WebSockets有足够的势头,浏览器供应商将改进支持(Chrome 14和Firefox 7/8支持最新的协议草案(,并且广泛的客户端和服务器框架将支持它
已经有了几个开源客户端库,包括Autobahn WebSocket。
当你想为自己烘焙一些东西(在原始TCP之上(时,我建议你的JSON消息使用前缀长度的格式,即Netstrings
免责声明:我是《高速公路》的作者,为Tavendo工作。
我已经把我和其他一些开发人员正在做的事情编成了代码:
http://en.wikipedia.org/wiki/Line_Delimited_JSON
它具有netcat/telnet兼容的优点。
另请参阅:http://ndjson.org/
消息的四个字节中的第一个可以是32位整数,表示消息的大小(以字节为单位(。然后接收器应遵循以下步骤:
- 读取数据的前四个字节,并计算出读取整个消息所需的确切字节数
- 读取消息的其余部分并将其反序列化为JSON
C#中的发件人代码:
public void WriteMessage(Packet packet) {
// Convert the object to JSON
byte[] message = Encoding.UTF8.GetBytes(packet.Serialize());
// Serialize the number of characters
byte[] messageLength = BitConverter.GetBytes(message.Length);
// Build the full message that will hold both the size of the message and the message itself
byte[] buffer = new byte[sizeof(int) + message.Length];
Array.Clear(message, 0, message.Length);
// Print the size into the buffer
for (int i = 0; i < sizeof(int); i++)
{
buffer[i] = messageLength[i];
}
// Print the message into the buffer
for (int i = 0; i < message.Length; i++)
{
buffer[i + sizeof(int)] = message[i];
}
// Send it
stream.Write(buffer, 0, buffer.Length);
}
您可以使用服务器发送的事件。
var source = new EventSource('/EventSource');
source.onmessage = function(e) {
var data = JSON.parse(e.data);
console.log(e.data);
};
source.onopen = function(e) {
console.log('EventSource opened');
};
source.onerror = function(e) {
console.log('EventSource error');
};