encoding - 如何在服务器端发送和接收 WebSocket 消息?

标签 encoding protocols websocket decoding

  • 如何根据协议(protocol)使用 WebSocket 在服务器端发送和接收消息?

  • 当我从浏览器向服务器发送数据时,为什么我在服务器上收到看似随机的字节?它是以某种方式编码的数据吗?

  • 框架如何在服务器→客户端和客户端→服务器方向上工作?

最佳答案

注意:这是一些关于如何实现一个非常简单的服务器的解释和伪代码,该服务器可以根据最终的帧格式处理传入和传出的 WebSocket 消息。它不包括握手过程。此外,这个答案是出于教育目的;它不是一个功能齐全的实现。

Specification (RFC 6455)

<小时/>

发送消息

(换句话说,服务器→浏览器)

您发送的帧需要根据 WebSocket 帧格式进行格式化。对于发送消息,格式如下:

  • 一个字节包含数据类型(以及一些超出普通服务器范围的附加信息)
  • 包含长度的一个字节
  • 如果长度不适合第二个字节,则为两个或八个字节(第二个字节是说明长度使用多少字节的代码)
  • 实际(原始)数据

第一个字节将是 1000 0001 (或 129 )用于文本框架。

第二个字节的第一位设置为 0因为我们没有对数据进行编码(从服务器到客户端的编码不是强制性的)。

需要确定原始数据的长度,以便正确发送长度字节:

  • 如果 0 <= length <= 125 ,您不需要额外的字节
  • 如果 126 <= length <= 65535 ,您需要两个额外的字节,第二个字节是 126
  • 如果 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)
<小时/>

接收消息

(换句话说,浏览器→服务器)

您获得的帧采用以下格式:

  • 包含数据类型的一个字节
  • 包含长度的一个字节
  • 如果长度不适合第二个字节,则附加两个或八个字节
  • 四个字节是掩码(=解码 key )
  • 实际数据

第一个字节通常并不重要 - 如果您只是发送文本,那么您只使用文本类型。它将是1000 0001 (或 129 )在这种情况下。

第二个字节和额外的两个或八个字节需要一些解析,因为您需要知道长度使用了多少字节(您需要知道真正的数据从哪里开始)。长度本身通常不是必需的,因为您已经有了数据。

第二个字节的第一位始终是 1这意味着数据被屏蔽(=编码)。从客户端到服务器的消息始终被屏蔽。您需要通过执行 secondByte AND 0111 1111 删除第一位。有两种情况,结果字节不代表长度,因为它不适合第二个字节:

  • 0111 1110 的第二个字节,或126 ,表示后面两个字节用于长度
  • 0111 1111 的第二个字节,或127 ,表示后面的八个字节用于长度

四个掩码字节用于解码已发送的实际数据。解码算法如下:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

哪里encodedByte是数据中的原始字节,encodedByteIndex是从真实数据的第一个字节开始计数的字节索引(偏移量),其索引为 0masks是一个包含四个掩码字节的数组。

这会产生以下用于解码的伪代码:

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

关于encoding - 如何在服务器端发送和接收 WebSocket 消息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8125507/

相关文章:

c++ - 自己的 C++ 网络协议(protocol) : How to define unique binary telegram values

html - Websockets 问题,也许是同源政策?

encoding - 在 Powershell 中,如何选择字符串的不同部分并将它们分别输出到变量并在行中显示?

algorithm - 放气算法伪代码

Android 媒体编解码器类型 "video/mp4v-es"- 是否与 MPEG-4 第 2 部分(MPEG-4 视觉效果)相同?

java - 从 hex64 转换为 base 64 字符串

iphone - UIAlertViewDelegate 扩展协议(protocol)

python - 用什么来替换 python 中的接口(interface)/协议(protocol)

javascript - 处理异步 JavaScript Chrome 控制台错误

javascript - Node websockets/ws - WSS : How to tell if I closed the connection, 或者他们做到了?