Android最佳实践——深入浅出WebSocket协议

人生处万类,知识最为贤。这篇文章主要讲述Android最佳实践——深入浅出WebSocket协议相关的知识,希望能为你提供帮助。
首先明确一下概念, WebSocket协议是一种建立在TCP连接基础上的全双工通信的协议。概念强调了两点内容:

  • TCP基础上
  • 全双工通信
那么什么是全双工通信呢?
全双工就是指客户端和服务端可以同时进行双向通信, 强调同时、双向通信
WebSocket可以应用于即时通信等场景, 比如现在直播很火热, 直播中的弹幕也可以使用WebSocket去实现。
WebSocket的协议内容可以见 The WebSocket Protocol, 讲得最全面的官方说明。简单介绍可以见维基百科WebSocket
在android客户端, 一般可以使用下面的库完成WebSocket通信
  • okhttp, 一般人我不告诉他okhttp还可以用来进行WebSocket通信
  • Java-WebSocket, 纯java实现的WebSocket客户端和服务端实现
那么在没有服务端支持的情况下, 我们客户端如何进行WebSocket的测试呢? 一般人我也不告诉他! 答案还是okhttp, 这次是okhttp的扩展模块mockserver, 不过最新版本的okhttp已经把WebSocket合入okhttp核心库中去了, 如果你用的版本比较低, 就可能需要依赖okhttp-ws模块。
先来看协议内容组成, 先上一张神图
Android最佳实践——深入浅出WebSocket协议

文章图片

【Android最佳实践——深入浅出WebSocket协议】WebSocket按上面图中协议规则进行传输, 上图称为一个数据帧。
  • FIN, 共1位, 标记消息是否是最后1帧, 1个消息由1个或多个数据帧构成, 若消息由1帧构成, 起始帧就是结束帧。
  • RSV1, RSV2, RSV3, 各1位, 预留位, 用于自定义扩展。如果没有扩展, 各位值为0; 如果定义了扩展, 即为非0值。如果接收的帧中此处为非0, 但是扩展中却没有该值的定义, 那么关闭连接。
  • OPCODE, 共4位, 帧类型, 分为控制帧和非控制帧。如果接收到未知帧, 接收端必须关闭连接。已定义的帧类型如下图所示:
    Android最佳实践——深入浅出WebSocket协议

    文章图片
除了上图中的0, 1, 2外( 0x0, 0x1, 0x2) , 3-7(0x3-0x7)暂时没有进行定义, 为以后的非控制帧保留。
除了上图中的8, 9, 10( 0x8, 0x9, 0xA)外, 11-15(0xB-0xF)暂时没有进行定义, 为以后的控制帧保留。
消息的分片, 一般来说, 对于一个长度较小的消息, 可以使用1帧完成消息的发送, 比如说文本消息, Fin的值为1, 表示结束, Opcode值不能为0, 0表示后续还有数据帧会发送过来。
而对于一些长度较长的消息, 则需要将消息进行分片发送。比如语音消息, 这时候起始帧的FIN值为0, Opcode为非0, 接着是若干帧( FIN值都为0, Opcode值为0) , 结束帧FIN值为1, Opcode值为0。
WebSocket的控制帧有3种, 关闭帧、Ping帧、Pong帧, 关闭帧很好理解, 客户端如果收到关闭帧直接关闭连接即可, 当然客户端也可以发送关闭帧给服务器端。而Ping帧和Pong帧则是WebSocket的心跳检测, 用于保证客户端是在线的, 一般来说, 只有服务端给客户端发送Ping帧, 然后客户端发送Pong帧进行回应, 表示自己还在线, 可以进行后续通信。
  • MASK, 共1位, 掩码位, 表示帧中的数据是否经过加密, 客户端发出的数据帧需要经过掩码处理, 这个值都是1。如果值是1, 那么Masking-key域的数据就是掩码秘钥, 用于解码PayloadData, 否则Masking-key长度为0。
WebSocket协议规定数据通过帧序列传输。客户端必须对其发送到服务器的所有帧进行掩码处理。
服务器一旦收到无掩码帧, 将关闭连接。服务器可能发送一个状态码是1002( 表示协议错误) 的Close帧。
而服务器发送客户端的数据帧不做掩码处理, 一旦客户端发现经过掩码处理的帧, 将关闭连接。客户端可能使用状态码1002。
更多状态码如下图所示:
Android最佳实践——深入浅出WebSocket协议

文章图片

  • Payload len, 7位或者7+ 16位或者7+ 64位, 表示数据帧中数据大小, 这里有好几种情况。
    • 如果值为0-125, 那么该值就是payload data的真实长度。
    • 如果值为126, 那么该7位后面紧跟着的2个字节就是payload data的真实长度。
    • 如果值为127, 那么该7位后面紧跟着的8个字节就是payload data的真实长度。
    • 长度遵循一个原则, 就是用最少的字节表示长度, 举个例子, 当payload data的真实长度是124时, 在0-125之间, 必须用7位表示; 不允许将这7位表示成126或者127, 然后后面用2个字节或者8个字节表示124, 这样做就违反了原则。
  • Masking-key , 0或者4个字节, 当MASK位为1时, 4个字节, 否则0个字节。如果MASK值为1, 则发出去的数据需要经过加密处理, 加密流程如下:

    推荐阅读