注意:这是一些关于如何实现一个非常简单的服务器的解释和伪代码,该服务器可以根据最终的帧格式处理传入和传出的 WebSocket 消息。它不包括握手过程。此外,这个答案是出于教育目的;它不是一个功能齐全的实现。
规范(RFC 6455) https://www.rfc-editor.org/rfc/rfc6455
发送消息
(换句话说,服务器→浏览器)
您发送的帧需要根据 WebSocket 帧格式进行格式化。对于发送消息,该格式如下:
- 包含数据类型的一个字节(以及一些超出普通服务器范围的附加信息)
- 包含长度的一个字节
- 如果长度不适合第二个字节,则为两个或八个字节(第二个字节是一个代码,说明长度使用了多少字节)
- 实际(原始)数据
第一个字节将是1000 0001
(or 129
)用于文本框架。
第二个字节的第一位设置为0
因为我们没有对数据进行编码(从服务器到客户端的编码不是强制性的)。
需要确定原始数据的长度,以便正确发送长度字节:
- if
0 <= length <= 125
,你不需要额外的字节
- if
126 <= length <= 65535
,您需要两个额外的字节,第二个字节是126
- if
length >= 65536
,您需要八个额外字节,第二个字节是127
长度必须被分割成单独的字节,这意味着您需要向右位移(八位),然后只保留最后八位AND 1111 1111
(这是255
).
长度字节之后是原始数据。
这导致以下伪代码:
bytesFormatted[0] = 129
indexStartRawData = -1 // it doesn't matter what value is
// set here - it will be set now:
if bytesRaw.length <= 125
bytesFormatted[1] = bytesRaw.length
indexStartRawData = 2
else if bytesRaw.length >= 126 and bytesRaw.length <= 65535
bytesFormatted[1] = 126
bytesFormatted[2] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[3] = ( bytesRaw.length ) AND 255
indexStartRawData = 4
else
bytesFormatted[1] = 127
bytesFormatted[2] = ( bytesRaw.length >> 56 ) AND 255
bytesFormatted[3] = ( bytesRaw.length >> 48 ) AND 255
bytesFormatted[4] = ( bytesRaw.length >> 40 ) AND 255
bytesFormatted[5] = ( bytesRaw.length >> 32 ) AND 255
bytesFormatted[6] = ( bytesRaw.length >> 24 ) AND 255
bytesFormatted[7] = ( bytesRaw.length >> 16 ) AND 255
bytesFormatted[8] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[9] = ( bytesRaw.length ) AND 255
indexStartRawData = 10
// put raw data at the correct index
bytesFormatted.put(bytesRaw, indexStartRawData)
// now send bytesFormatted (e.g. write it to the socket stream)
接收消息
(换句话说,浏览器→服务器)
您获得的帧采用以下格式:
- 包含数据类型的一个字节
- 包含长度的一个字节
- 如果长度不适合第二个字节,则附加两个或八个字节
- 四个字节是掩码(=解码密钥)
- 实际数据
第一个字节通常并不重要 - 如果您只是发送文本,那么您只使用文本类型。这将是1000 0001
(or 129
) 在这种情况下。
第二个字节和附加的两个或八个字节需要进行一些解析,因为您需要知道长度使用了多少字节(您需要知道真正的数据从哪里开始)。长度本身通常不是必需的,因为您已经拥有数据。
第二个字节的第一位始终是1
这意味着数据被屏蔽(=编码)。从客户端到服务器的消息始终被屏蔽。您需要通过执行以下操作来删除第一位secondByte AND 0111 1111
。有两种情况,结果字节不代表长度,因为它不适合第二个字节:
- 第二个字节
0111 1110
, or 126
,表示后面两个字节用于长度
- 第二个字节
0111 1111
, or 127
,表示后面的八个字节用于长度
四个掩码字节用于解码已发送的实际数据。解码算法如下:
decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]
where encodedByte
是数据中的原始字节,encodedByteIndex
是从第一个字节开始计数的字节索引(偏移量)真实数据的,其中有索引0
. masks
是包含四个掩码字节的数组。
这导致了以下用于解码的伪代码:
secondByte = bytes[1]
length = secondByte AND 127 // may not be the actual length in the two special cases
indexFirstMask = 2 // if not a special case
if length == 126 // if a special case, change indexFirstMask
indexFirstMask = 4
else if length == 127 // ditto
indexFirstMask = 10
masks = bytes.slice(indexFirstMask, 4) // four bytes starting from indexFirstMask
indexFirstDataByte = indexFirstMask + 4 // four bytes further
decoded = new array
decoded.length = bytes.length - indexFirstDataByte // length of real data
for i = indexFirstDataByte, j = 0; i < bytes.length; i++, j++
decoded[j] = bytes[i] XOR masks[j MOD 4]
// now use "decoded" to interpret the received data