websocket协议分析

来源:互联网 发布:淘宝追加好评长心吗 编辑:程序博客网 时间:2024/05/16 17:02

websocket基本上是一个很简单的协议, 主要流程非常少, 实现起来也很简单.  


为简单起见, 下面只分析握手和数据帧的报文.


1.  握手(handshake).


    握手协议由客户端发起, 服务器响应, 一来一回就完成了. 基本上是为了兼容现有的http基础设施. 

    下面是一个客户端发起的握手请求:

47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A GET./.HTTP/1.1..55 70 67 72 61 64 65 3A 20 77 65 62 73 6F 63 6B Upgrade:.websock65 74 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 et..Connection:.55 70 67 72 61 64 65 0D 0A 48 6F 73 74 3A 20 31 Upgrade..Host:.139 32 2E 31 36 38 2E 38 2E 31 32 38 3A 31 33 30 92.168.8.128:13030 0D 0A 4F 72 69 67 69 6E 3A 20 6E 75 6C 6C 0D 0..Origin:.null.0A 50 72 61 67 6D 61 3A 20 6E 6F 2D 63 61 63 68 .Pragma:.no-cach65 0D 0A 43 61 63 68 65 2D 43 6F 6E 74 72 6F 6C e..Cache-Control3A 20 6E 6F 2D 63 61 63 68 65 0D 0A 53 65 63 2D :.no-cache..Sec-57 65 62 53 6F 63 6B 65 74 2D 4B 65 79 3A 20 64 WebSocket-Key:.d33 35 39 46 64 6F 36 6F 6D 79 71 66 78 79 59 46 359Fdo6omyqfxyYF37 59 61 63 77 3D 3D 0D 0A 53 65 63 2D 57 65 62 7Yacw==..Sec-Web53 6F 63 6B 65 74 2D 56 65 72 73 69 6F 6E 3A 20 Socket-Version:.31 33 0D 0A 53 65 63 2D 57 65 62 53 6F 63 6B 65 13..Sec-WebSocke74 2D 45 78 74 65 6E 73 69 6F 6E 73 3A 20 78 2D t-Extensions:.x-77 65 62 6B 69 74 2D 64 65 66 6C 61 74 65 2D 66 webkit-deflate-f72 61 6D 65 0D 0A 55 73 65 72 2D 41 67 65 6E 74 rame..User-Agent3A 20 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 57 :.Mozilla/5.0.(W69 6E 64 6F 77 73 20 4E 54 20 36 2E 31 3B 20 57 indows.NT.6.1;.W4F 57 36 34 29 20 41 70 70 6C 65 57 65 62 4B 69 OW64).AppleWebKi74 2F 35 33 37 2E 33 36 20 28 4B 48 54 4D 4C 2C t/537.36.(KHTML,20 6C 69 6B 65 20 47 65 63 6B 6F 29 20 43 68 72 .like.Gecko).Chr6F 6D 65 2F 33 32 2E 30 2E 31 36 35 33 2E 30 20 ome/32.0.1653.0.53 61 66 61 72 69 2F 35 33 37 2E 33 36 0D 0A 0D Safari/537.36...0A 

    0D 0A 0D 0A, 也就是用"\r\n\r\n"收尾, 这和http头没什么区别.  转换成字符串就是:

GET / HTTP/1.1Upgrade: websocketConnection: UpgradeHost: 192.168.8.128:1300Origin: nullPragma: no-cacheCache-Control: no-cacheSec-WebSocket-Key: d359Fdo6omyqfxyYF7Yacw==Sec-WebSocket-Version: 13Sec-WebSocket-Extensions: x-webkit-deflate-frameUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1653.0 Safari/537.36
    其中有一对重要的kv, 就是Sec-WebSocket-Key: d359Fdo6omyqfxyYF7Yacw==,  看上去是一个base64编码后的结果, 服务器需要

    对这个sec-key作一些处理, 并返回握手响应, 这个处理是:

byte[] sha = sha1(("d359Fdo6omyqfxyYF7Yacw==" + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes());System.out.println(new String(Base64.getEncoder().encode(sha)));
   也就是原封不动的拿着这个sec-key和另一个神奇的字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"相连, 再经过sha1摘要

    算法处理, 最后再经过base64编码输出即可, 上面的输出结果应该是: pLO2KC7b5t0TZl1E6A3sqJ6EzU4=


   服务器在收到握手请求后, 如果愿意提供服务, 则返回一个握手响应, 如下:

HTTP/1.1 101 Switching ProtocolsConnection: UpgradeUpgrade: WebSocketSec-WebSocket-Accept: pLO2KC7b5t0TZl1E6A3sqJ6EzU4=
    遵循http的规则, 节流上一样是要以"\r\n\r\n"收尾.



二. 数据帧

    rfc6455上叫做非控制帧, 除了非控制帧之外, 就是控制帧. 包括connection close, ping, pong等帧, 这里只讲非控制帧, 也就是数据帧.

    数据帧从长度上可以分为三种. 帧中的静荷数据(payload data)长度小于0x7E的为小帧, 静荷数据长度 >=0x7E又<=0x10000的为中帧,

    再长的叫大帧.

    数据帧从类型上暂时可以分为两种, 文本帧和二进制帧.

    例子:

    a). 一个从客户端发向服务端的小帧.

82 B0 6A F7 C6 30 0A D9 C6 34 D4 18 78 C1 6E F5 ..j..0...4..x.n.C6 30 6C D5 CC 10 23 87 AF 48 3C A2 9C 64 01 C4 .0l...#..H<..d..AE 59 04 C5 B1 5B 35 85 A3 41 18 B0 F5 5C 13 8E .Y...[5..A...\..92 42 02 84 85 53                               .B...S
   82

    二进制为: 1000 0010, 最高位(FIN)为1, 表示这是最后一帧, 第一个帧也可能是最后一帧. 身后还有三位为预留. 低位四0010为操作码. 

    也就是0x02, 表示这是一个二进制帧, 0x01为文本帧.


    B0

    二进制为: 1011 0000, 最高位(MASK)为1, 表示当前帧的静荷数据部分使用了掩码,  事实上, rfc6455规定从客户端发往服务器端的数据帧

    必需使用掩码, 反过来, 从服务器发回来的, 则必需不使用掩码. 低7位为静荷数据长度字段, 这里是011 0000, 也就是0x30, 从上面的报文上

   看, 这个0x30没有包含后面的掩码.


    6A F7 C6 30

    掩码, 掩码总是四个字节.


    0A D9 C6...一直到最后为经过掩码加工后的静荷数据.  要回到数据本来的面目, 使用下面的算法:

byte by[] = new byte[]{0x82, 0xB0, 0x6A, 0xF7, 0xC6, 0x30, 0x0A....};byte mask[] = new byte[] { 0x6A, (byte) 0xF7, (byte) 0xC6, 0x30 };for (int i = 6 /* 越过掩码. */; i < by.length; i++)by[i] = (byte) (by[i] ^ mask[(i - 6) % 4]);
    得到的结果应该是:

82 B0 6A F7 C6 30 60 2E 00 04 BE EF BE F1 04 02 ..j..0`.........00 00 06 22 0A 20 49 70 69 78 56 55 5A 54 6B 33 ..."..IpixVUZTk368 69 6E 32 77 6B 5F 72 65 71 72 47 33 6C 79 79 hin2wk_reqrG3lyy54 72 68 73 43 63                               TrhsCc

    b). 一个从服务器发给客户端的小帧.

82 29 61 27 01 04 BE EF BE F1 05 02 00 00 06 1B .)a'............0A 08 55 3B 02 19 39 35 E2 44 12 0F 21 EC BC 47 ..U;..95.D..!..G02 F3 EC 70 ED 5B 7B 07 C7 F4 D0                ...p.[{....
    更简单了, 还是82, 最后一帧, 二进制帧,  29, 0010 1001, 无掩码, 也就是身后全长为0x29.


    c). 未使用掩码的中帧.   

    81 7E 01 00 66 77 88 ..., 帧长为 0x0100, 也就是256个字节.


    d). 未使用掩码的大帧.

    82 7F 00 00 00 00 11 22 33 44 66 77 88 ..., 帧长为0x0000000011223344, 直接跳过4字节, 而使用8字节来表示长度, 非常暴力.


    这里需要注意的是, websocket要求使用最小帧原则, 也就是静荷数据长度小于0x7E帧, 不能使用中帧或大帧的来表示.  长度小于

    0x10000的帧也不能用大帧来表示.








0 0
原创粉丝点击