背景

现在写客户端或者网页的时候, 越来越多的需要与长连接打交道, 尤其是在这个老板动不动就要搞一个聊天系统的时代, 后端大哥们于是分分钟就能造一个基于TCP或者WebSockets的消息协议出来. 但是问题在于每做一个新项目, 后端大哥们就能造出一个新协议, 而且能有各种神奇的限制. 比如说要在长连接当中保持一个状态机, 发送某条消息后收到的下一条消息一定是XXX, 或者完全一个JSON就直接丢了出来等等. 虽然都能用, 但是却需要在各种地方维护着不同的底层通信库, 没有章法可依, 所以草拟了这个协议.

简介

STMP最简单的消息协议(The simplest message protocol)

简单来说, STMP有以下特点:

  • 非常精简的固定头部, 仅有一字节(二进制序列化)
  • 支持二进制序列化(TCP)以及文本序列化(WebSockets), 文本序列化支持消息分包传送(传递二进制数据)
  • 与IP协议掩码类似的上层路由控制
  • 负载编码格式对协议透明
  • 心跳检测
  • 四种消息类型: 心跳, 请求, 通知, 回复
  • 与HTTP协议类似的返回状态码控制
A lightweight messaging protocol for small sensors and mobile devices, optimized for high-latency or unreliable networks固定消息头只有2字节QoS服务质量控制QoSProtocolBuffersHTTP 2

消息字段定义

一个全双工的通信系统中, 双端需要有效识别对方发来的消息, 并作出相应的处理, 选择是否回应等操作, 所以除了实际的负载之外, 还需要若干标志字段. STMP中, 完整的消息字段列表如下, 需要注意的是并不是每条消息都会包含所有的这些字段, 需要根据网络环境以及消息类型确定应该包含的字段列表. 但是如果某条消息包含了以下这些字段中的某一些字段的话,排序顺序一定与字段在下面出现的顺序相同.

消息类型(KIND)0123消息编码格式(ENCODING)0-70PSPAYLOAD12345消息ID(ID)0x0000-0xFFFF消息请求动作(ACTION)0x00000000-0xFFFFFFFFxxx.xxx.xxx.xxx0x00-0xFF0x00状态码(STATUS)0x00-0xFF0x00-0x7FACTION0x80-0xFFACTION0x000x100x110x120x200x210x220x230x240x250x260x270x300x310x320x330x340x35负载长度(PS)PAYLOAD0x00000000-0xFFFFFFFF4GbENCODINGENCODING0负载(PAYLOAD)PSENCODING

消息类型

如前所述, STMP中消息分类四种类型, 不同的消息类型可能包含的字段及含义有所不同, 详细如下:

心跳消息

KIND

请求消息

STATUS0x25KINDENCODINGIDACTIONPSPAYLOADSTATUS

通知消息

此消息表示发送方向接收方发送一个通知, 接收方无需回复此消息.

KINDENCODINGACTIONPSPAYLOADIDSTATUS

回复消息

请求消息ID请求消息IDSTATUS0x34
KINDENCODINGIDSTATUSPSPAYLOADACTION

消息序列化

ArrayBuffer

二进制序列化

KINDENCODINGKIND0ENOCDING0心跳消息
|   0 ... 7   |  8 ... 15  |  16 ... 23  |  24 ... 31  |
| FixedHeader |           ID             |    ACTION   |
|               ACTION                   |    STATUS   |
|                         PS                           |
|                 PAYLOAD    ...                       |
IDACTIONPSBigEndian
|   0   |   1   |   2   |   3   |   4   |   5   |   6   |   7   |
|     KIND      |       ENCODING        |   0   |   0   |   0   |

最后三个位为保留位(未用到), 全部置零.

文本就序列化

|
KIND(1)|ENCODING(1)|ID?(1-5)|ACTION?(1-10)|STATUS?(1-3)|PS?(1-10)|PAYLOAD?(...)
PAYLOADENCODING0PAYLOADWebSockets
心跳消息KIND"0"

区分文本消息与二进制消息

'0''1''2''3'0x30-0x330x000x40KIND00b01000000

版本协商

MAJORMINOR0150x00xFMAJOR.MINOR
0.1
0x000RawOkVersionNotSupported

版本号序列化

MAJORMINORMAJORMINOR

实现

目前仅实现了Golang和JS的简单的消息编解码部分, 地址在: go版本, js版本, 还有很多工作要做T_T, 如果有人提PR就好了?????.