WebScoket支持safari+chrome+firefox的规范和协议

WebScoket 规范

4.1 握手协议

websocket 是 独立的基于TCP的协议, 其跟http协议的关系仅仅是 WebSocket 的握手被http 服务器当做 Upgrade request http包处理。 websocket 有自己的握手处理。 TCP连接建立后,client 发送websocket 握手请求. 请求包需求如下:

  • 必须是有效的http request 格式
  • HTTP request method 必须是GET,协议应不小于1.1 如: Get /chat HTTP/1.1
  • 必须包括Upgrade 头域,并且其值为“websocket”
  • 必须包括"Connection" 头域,并且其值为 "Upgrade"
  • 必须包括"Sec-WebSocket-Key"头域,其值采用base64编码的随机16字节长的字符序列, 服务器端根据该域来判断client 确实是websocket请求而不是冒充的,如http。响应方式是,首先要获取到请求头中的Sec-WebSocket-Key的值,再把这一段 GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"加到获取到的Sec-WebSocket-Key的值的后面,然后拿这个 字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密,就得到了返回给客户端的Sec-WebSocket-Accept的http响应头的值。
  • 如果请求来自浏览器客户端,还必须包括Origin头域 。 该头域用于防止未授权的跨域脚本攻击,服务器可以从Origin决定是否接受该WebSocket连接。
  • 必须包括"Sec-webSocket-Version" 头域,当前值必须是13.
  • 可能包括"Sec-WebSocket-Protocol",表示client(应用程序)支持的协议列表,server选择一个或者没有可接受的协议响应之。
  • 可能包括"Sec-WebSocket-Extensions", 协议扩展, 某类协议可能支持多个扩展,通过它可以实现协议增强
  • 可能包括任意其他域,如cookie

示例如下:

GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13

Server 接手到握手请求后应处理该请求包括:

  • 处理请求包括处理GET 方法
  • 验证Upgrader头域
  • 验证Connection 头域
  • 处理Sec-WebSocket-Key头域,方法见上;
  • 处理Sec-WebSocket-Version
  • 处理Origin头域,可选, 浏览器必须发送该头域
  • 处理Sec-WebSocket-Protocol头域,可选
  • 处理Sec-WebSocket-Extensions 头域,可选
  • 处理其他头域,可选
  • Server 发送握手响应,这里只介绍服务器接受该连接情况下,包括:
  • http Status-Line
  • Upgrade 头域 ,值必须是"websocket"
  • Conntion头域,值必须是:“Upgrade”
  • Sec-WebSocket-Accept” 头域,该头域的值即处理Sec-WebSocket-Key" 域后的结果。
  • 可选的"Sec-WebSocket-Protocol"头域
  • 可选的"Sec-WebSocket-Extensions"头域

响应可能如下:

HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat

4.2 数据传输

该节主要参考了 http://blog.csdn.net/fenglibing/article/details/6852497。  在WebSocket 协议中,使用序列frames方式来传输数据。一个frame的标准格式如下:

0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

FIN:1位,是否是消息的结束帧(分片)

RSV1, RSV2, RSV3: 分别都是1位, 预留,用于约定自定义协议。 如果双方之间没有约定自定义协议,那么这几位的值都必须为0,否则必须断掉WebSocket连接;

Opcode:4位操作码,定义有效负载数据,如果收到了一个未知的操作码,连接也必须断掉,以下是定义的操作码:

%x0 表示连续消息分片
%x1 表示文本消息分片

%x2 表未二进制消息分片

%x3-7 为将来的非控制消息片断保留的操作码

%x8 表示连接关闭  %x9 表示心跳检查的ping

%xA 表示心跳检查的pong

%xB-F 为将来的控制消息片断的保留操作码

Mask: 定义传输的数据是否有加掩码,如果设置为1,掩码键必须放在masking-key区域,客户端发送给服务端的所有消息,此位的值都是1;

Payload length: 传输数据的长度,以字节的形式表示:7位、7+16位、或者7+64位。如果 这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;如果这个值是126,则随后的两个字节表示的是一个16进制无符号数,用来表示传 输数据的长度;如果这个值是127,则随后的是8个字节表示的一个64位无符合数,这个数用来表示传输数据的长度。多字节长度的数量是以网络字节的顺序表 示。负载数据的长度为扩展数据及应用数据之和,扩展数据的长度可能为0,因而此时负载数据的长度就为应用数据的长度。注意Payload length不包括Masking-key在内。

Masking-key: 0或4个字节,客户端发送给服务端的数据,都是通过内嵌的一个32位值作为掩码的;掩码键只有在掩码位设置为1的时候存在。 数据Mask方法是,第 i byte 数据 = orig-data ^ (i % 4) .

Payload data: (x+y)位,负载数据为扩展数据及应用数据长度之和。

Extension data:x位,如果客户端与服务端之间没有特殊约定,那么扩展数据的长度始终为0,任何的扩展都必须指定扩展数据的长度,或者长度的计算方式,以及在握手时如何确定正确的握手方式。如果存在扩展数据,则扩展数据就会包括在负载数据的长度之内。

Application data:y位,任意的应用数据,放在扩展数据之后,应用数据的长度=负载数据的长度-扩展数据的长度。

把消息分片处理主要是处于以下两个原因:

  • 消息接收方事先并不知道消息大小, 而且也没必要预留一个足够大的buffer来处理;
  • multiplexing

消息分片一些规则如下(不全):

  • 为分片消息(single-frame) 其FIN置为1,并且opcode code 不是 0;
  • 分片消息序列如下, 第一帧FIN置为0,opcode code不是0; 接着是FIN置为0,opcode code也是0; 最后帧 FIN为1,opcode code为0.
  • 在分片消息发送期间可能插入了控制帧
  • 控制帧不能分片

控制帧的opcode符号位为1, 目前控制帧包括 0×8(Close), 0×9(Ping) 0xA (Pong). 0xB-0xF 被预留。

详细解析如下,来自http://blog.csdn.net/fenglibing/article/details/6852497:

 ws-frame                = frame-fin
                             frame-rsv1
                             frame-rsv2
                             frame-rsv3
                             frame-opcode
                             frame-masked
                             frame-payload-length
                             [ frame-masking-key ]
                             frame-payload-data
   frame-fin               = %x0 ; 表示这不是当前消息的最后一帧,后面还有消息
                           / %x1 ; 表示这是当前消息的最后一帧
   frame-rsv1              = %x0
                             ; 1 bit, 如果没有扩展约定,该值必须为0
   frame-rsv2              = %x0
                             ; 1 bit, 如果没有扩展约定,该值必须为0
   frame-rsv3              = %x0
                             ; 1 bit, 如果没有扩展约定,该值必须为0
   frame-opcode            = %x0 ; 表示这是一个连续帧消息
                           / %x1 ; 表示文本消息
                           / %x2 ; 表示二进制消息
                           / %x3-7 ; 保留
                           / %x8 ; 表示客户端发起的关闭
                           / %x9 ; ping(用于心跳)
                           / %xA ; pong(用于心跳)
                           / %xB-F ; 保留
   frame-masked            = %x0 ; 数据帧没有加掩码,后面没有掩码key
                           / %x1 ; 数据帧加了掩码,后面有掩码key
   frame-payload-length    = %x00-7D
                           / %x7E frame-payload-length-16
                           / %x7F frame-payload-length-63
			   ; 表示数据帧的长度
   frame-payload-length-16 = %x0000-FFFF
			   ; 表示数据帧的长度
   frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
			   ; 表示数据帧的长度
   frame-masking-key       = 4( %0x00-FF ) ; 掩码key,只有当掩码位为1时出现
   frame-payload-data      = (frame-masked-extension-data
                              frame-masked-application-data) 
 ; 当掩码位为1时,这里的数据为带掩码的数据,扩展数据及应用数据都带掩码
                           / (frame-unmasked-extension-data
                              frame-unmasked-application-data) ;
 当掩码位为0时,这里的数据为不带掩码的数据,扩展数据及应用数据都不带掩码

   frame-masked-extension-data     = *( %x00-FF ) ; 目前保留,以后定义
   frame-masked-application-data   = *( %x00-FF )
   frame-unmasked-extension-data   = *( %x00-FF ) ; 目前保留,以后定义
   frame-unmasked-application-data = *( %x00-FF )

 

Close 处理

Close 帧的opcode是0×8. 接收到 Close 帧后,如果之前没发送过Close帧,则其必须发送Close 帧响应,但其可以延迟发送Close响应帧,例如在其发送完数据之后发送;但是,协议不保证对方在发送Close 帧后仍会处理其后续的数据。Close帧可能Client发起也可能是Server发起。

Ping-Pong 帧

接收到Ping帧后将响应Pong帧, 主要用于检测网络连接情况。

Extensions

WebSocket 支持协议扩展。 例如增加一个认证处理或者速率控制等,这通过client-server 协商完成。在WebSocket 握手处理时,通过头域Sec-WebSocket-Extensions来完成协商。 例如:

Sec-WebSocket-Extensions: mux; max-channels=4; flow-control,
          deflate-stream

服务器接收一个或多个extensiions 通过再起响应的Sec-WebSocket-Extensions头域增加一个或多个extension完成。

 

说明:

 

服务器建立成功之后,如果有客户端请求连接本服务器,需要用socket_accept等方法建立一个新的socket连接,并接收客户端的请求信息,处理之后,返回响应信息,然后握手成功。  

接下来是字符串通信,客户端send过来一段字符串信息,服务器端接收到并返回给客户端这个字符串。   首先我们处理接收到的信息,根据上篇文章介绍的数据传输格式,并firefox的FIN一直为1,RSV1,2,3为0,如果是文本消息,那么 opcode为1,所以数据包的第一个数据是0x81,然后是一位mask值,firefox发来的数据是加了掩码的,所以mask值为1,后面跟7位是 数据信息长度,我们以客户端发送hi为例,那么长度就是2个字节,则第二个数据就是0x82,这里没有约定扩展数据,所以不存在扩展数据长度字节,接下来 是4个数据的掩码(因为我们这里是发送hi,2个字节的信息,小于125个字节,所以掩码是第3-第6个数据,根据数据长度的不同,掩码的位置也不同,如 果取到那7位表示的值是126,则掩码为第5-第8个数据,如果取到那7位表示的值是127,则掩码为第11-第14个数据),后面跟客户端发送的内容数 据,处理接收到的数据我们需要用取到的掩码依次轮流跟内容数据做异或(^)运算,第一个内容数据与第一个掩码异或,第二个内容数据与第二个掩码异或……第 五个内容数据与第一个掩码异或……以此类推,一直到结束,然后对内容进行编码。

 

根据数据长度的不同,掩码的位置也不同:

从第9个字节开始是 1111101=125,掩码是第3-第6个数据

从第9个字节开始是 1111110=126,掩码是第5-第8个数据

从第9个字节开始是 1111111=126,掩码是第11-第14个数据

 

举例一:

hi
 1000000110000010 1101011011101001
 111110 111000 10111110 10000000
 111110 111001 11010110 11101001
                1101000  1101001
                
         [0]    129    byte
         [1]    130    byte
         [2]    214    byte
         [3]    233    byte
         [4]    62     byte
         [5]    56     byte
         [6]    190     byte
         [7]    128     byte
 
 
 1234567890                 
         [0]    129    byte
         [1]    138    byte
         
         [2]    108    byte
         [3]    255    byte
         [4]    86    byte
         [5]    166    byte
         
         [6]    93    byte
         [7]    205    byte
         [8]    101    byte
         [9]    146    byte
         [10]    89    byte
         [11]    201    byte
         [12]    97    byte
         [13]    158    byte
         [14]    85    byte
         [15]    207    byte


 举例二:

 01234567890123456789012345678901234567890123456789
 01234567890123456789012345678901234567890123456789
 01234567890123456789012345678901234567890123456789
 01234567890123456789012345678901234567890123456789
 
 01234567890123456789012345678901
 10000001111111100110010011001101
 
         [0]    129    byte
         [1]    254    byte
         [2]    0    byte
         [3]    201    byte
         
         [4]    77    byte
         [5]    175    byte
         [6]    124    byte
         [7]    107    byte
         
         [8]    125    byte
         [9]    158    byte
         [10]    78    byte
         [11]    88    byte
         [12]    121    byte
         [13]    154    byte
         [14]    74    byte
         [15]    92    byte
         [16]    117    byte
         [17]    150    byte
         [18]    76    byte
         [19]    90    byte
         [20]    127    byte
         [21]    156    byte
         [22]    72    byte
         [23]    94    byte
         [24]    123    byte
         [25]    152    byte
         [26]    68    byte
         [27]    82    byte
         [28]    125    byte
         [29]    158    byte
         [30]    78    byte
         [31]    88    byte
         [32]    121    byte
         [33]    154    byte
         [34]    74    byte
         [35]    92    byte
         [36]    117    byte
         [37]    150    byte
         [38]    76    byte
         [39]    90    byte
         [40]    127    byte
         [41]    156    byte
         [42]    72    byte
         [43]    94    byte
         [44]    123    byte
         [45]    152    byte
         [46]    68    byte
         [47]    82    byte
         [48]    125    byte
         [49]    158    byte
         [50]    78    byte
         [51]    88    byte
         [52]    121    byte
         [53]    154    byte
         [54]    74    byte
         [55]    92    byte
         [56]    117    byte
         [57]    150    byte
         [58]    76    byte
         [59]    90    byte
         [60]    127    byte
         [61]    156    byte
         [62]    72    byte
         [63]    94    byte
         [64]    123    byte
         [65]    152    byte
         [66]    68    byte
         [67]    82    byte
         [68]    125    byte
         [69]    158    byte
         [70]    78    byte
         [71]    88    byte
         [72]    121    byte
         [73]    154    byte
         [74]    74    byte
         [75]    92    byte
         [76]    117    byte
         [77]    150    byte
         [78]    76    byte
         [79]    90    byte
         [80]    127    byte
         [81]    156    byte
         [82]    72    byte
         [83]    94    byte
         [84]    123    byte
         [85]    152    byte
         [86]    68    byte
         [87]    82    byte
         [88]    125    byte
         [89]    158    byte
         [90]    78    byte
         [91]    88    byte
         [92]    121    byte
         [93]    154    byte
         [94]    74    byte
         [95]    92    byte
         [96]    117    byte
         [97]    150    byte
         [98]    76    byte
         [99]    90    byte
         [100]    127    byte
         [101]    156    byte
         [102]    72    byte
         [103]    94    byte
         [104]    123    byte
         [105]    152    byte
         [106]    68    byte
         [107]    82    byte
         [108]    125    byte
         [109]    158    byte
         [110]    78    byte
         [111]    88    byte
         [112]    121    byte
         [113]    154    byte
         [114]    74    byte
         [115]    92    byte
         [116]    117    byte
         [117]    150    byte
         [118]    76    byte
         [119]    90    byte
         [120]    127    byte
         [121]    156    byte
         [122]    72    byte
         [123]    94    byte
         [124]    123    byte
         [125]    152    byte
         [126]    68    byte
         [127]    82    byte
         [128]    125    byte
         [129]    158    byte
         [130]    78    byte
         [131]    88    byte
         [132]    121    byte
         [133]    154    byte
         [134]    74    byte
         [135]    92    byte
         [136]    117    byte
         [137]    150    byte
         [138]    76    byte
         [139]    90    byte
         [140]    127    byte
         [141]    156    byte
         [142]    72    byte
         [143]    94    byte
         [144]    123    byte
         [145]    152    byte
         [146]    68    byte
         [147]    82    byte
         [148]    125    byte
         [149]    158    byte
         [150]    78    byte
         [151]    88    byte
         [152]    121    byte
         [153]    154    byte
         [154]    74    byte
         [155]    92    byte
         [156]    117    byte
         [157]    150    byte
         [158]    76    byte
         [159]    90    byte
         [160]    127    byte
         [161]    156    byte
         [162]    72    byte
         [163]    94    byte
         [164]    123    byte
         [165]    152    byte
         [166]    68    byte
         [167]    82    byte
         [168]    125    byte
         [169]    158    byte
         [170]    78    byte
         [171]    88    byte
         [172]    121    byte
         [173]    154    byte
         [174]    74    byte
         [175]    92    byte
         [176]    117    byte
         [177]    150    byte
         [178]    76    byte
         [179]    90    byte
         [180]    127    byte
         [181]    156    byte
         [182]    72    byte
         [183]    94    byte
         [184]    123    byte
         [185]    152    byte
         [186]    68    byte
         [187]    82    byte
         [188]    125    byte
         [189]    158    byte
         [190]    78    byte
         [191]    88    byte
         [192]    121    byte
         [193]    154    byte
         [194]    74    byte
         [195]    92    byte
         [196]    117    byte
         [197]    150    byte
         [198]    76    byte
         [199]    90    byte
         [200]    127    byte
         [201]    156    byte
         [202]    72    byte
         [203]    94    byte
         [204]    123    byte
         [205]    152    byte
         [206]    68    byte
         [207]    82    byte
         [208]    71    byte


 

代码分析掩码:

复制代码
  /// <summary>
 ///判断传入数据是否存在掩码
 /// 传入数据:hi
 /// socket接收到的二进制数据:
 ///      1000000110000010 1101011011101001
 ///      111110 111000 10111110 10000000
 /// 掩码异或的操作:
 ///             111110 111000 10111110 10000000
 ///   进行异或^ 111110 111001 11010110 11101001 
 ///    结果:                   1101000  1101001
 /// 数据样例:
 ///        [0]    129    byte
 ///        [1]    130    byte
 ///        [2]    214    byte
 ///        [3]    233    byte
 ///        [4]    62     byte
 ///        [5]    56     byte
 ///        [6]    190     byte
 ///        [7]    128     byte
 /// </summary>
 /// <returns></returns>
         private string UnWrap()
         {
             string result = string.Empty;
 
             // 计算非空位置
             int lastStation = GetLastZero();
 
             // 利用掩码对org-data进行异或
             int frame_masking_key = 1;
             for (int i = 6; i <= lastStation; i++)
             {
                 frame_masking_key = i % 4;
                 frame_masking_key = frame_masking_key == 0 ? 4 : frame_masking_key;
                 frame_masking_key = frame_masking_key == 1 ? 5 : frame_masking_key;
                 receivedDataBuffer[i] = Convert.ToByte(receivedDataBuffer[i] ^ receivedDataBuffer[frame_masking_key]);
             }
 
             System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
             result = decoder.GetString(receivedDataBuffer, 6, lastStation - 6 + 1);
 
             return result;
 
         }
复制代码

 

 

复制代码
 /// <summary>
 /// 对传入数据进行无掩码转换
 /// </summary>
 /// <returns></returns>
         public static byte[] Wrap(string msg, int maxBufferSize)
         {
             // 掩码开始位置
             int masking_key_startIndex = 2;
 
             byte[] msgByte = Encoding.UTF8.GetBytes(msg);
 
             // 计算掩码开始位置
             if (msgByte.Length <= 125)
             {
                 masking_key_startIndex = 2;
             }
             else if (msgByte.Length > 65536)
             {
                 masking_key_startIndex = 10;
             }
             else if (msgByte.Length > 125)
             {
                 masking_key_startIndex = 4;
             }
 
             // 创建返回数据
             byte[] result = new byte[msgByte.Length + masking_key_startIndex];
 
             // 开始计算ws-frame
         // frame-fin + frame-rsv1 + frame-rsv2 + frame-rsv3 + frame-opcode
             result[0] = 0x81;       // 129
 
             // frame-masked+frame-payload-length
             // 从第9个字节开始是 1111101=125,掩码是第3-第6个数据
             // 从第9个字节开始是 1111110>=126,掩码是第5-第8个数据
             if (msgByte.Length <= 125)
             {
                 result[1] = Convert.ToByte(msgByte.Length);
             }
             else if (msgByte.Length > 65536)
             {
                 result[1] = 0x7F;   // 127
             }
             else if (msgByte.Length > 125)
             {
                 result[1] = 0x7E;   // 126
                 result[2] = Convert.ToByte(msgByte.Length >> 8);
                 result[3] = Convert.ToByte(msgByte.Length % 256);
             }
 
             // 将数据编码放到最后
             Array.Copy(msgByte, 0, result, masking_key_startIndex, msgByte.Length);
 
             return result;
         }
复制代码


 

 WebSocket 协议:

 

复制代码
public enum WebSocketProtocol
    { /* * 
         * Request
            GET /WebIM5?uaid=200513807p8912-8de8c7e2-c963-4f67-8aca-8028797efbc1&re=0 HTTP/1.1
            Upgrade: WebSocket
            Connection: Upgrade
            Host: 10.10.150.60:5002
            Origin: https://localhost:444
            Sec-WebSocket-Key1: 3+3 1  8kgV"m 0 8  64u43
            Sec-WebSocket-Key2: 3_7891 6 4 `50 `8
         * 
         * Response
            HTTP/1.1 101 WebSocket Protocol Handshake
            Upgrade: WebSocket
            Connection: Upgrade
            Sec-WebSocket-Origin: https://localhost:444
            Sec-WebSocket-Location: ws://192.168.110.....
            Sec-WebSocket-Protocol: WebIM5
         * 
         *  asdfalskdfa
         * */ draft_00 = 0, /* * 
         * Request
            GET /WebIM5?uaid=200513807p8912-2e695e5b-9b46-4511-b59e-28981b4ab327&re=0 HTTP/1.1
            Upgrade: websocket
            Connection: Upgrade
            Host: 10.10.150.60:5002
            Origin: https://localhost:444
            Sec-WebSocket-Key: 1o4Jk9XPGvTX66OxmNMaww==
            Sec-WebSocket-Version: 13
         * 
         * Response
            HTTP/1.1 101 Switching Protocols
            Upgrade: websocket
            Connection: Upgrade
            Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
            Sec-WebSocket-Protocol: WebIM5
         * */ draft_17 = 17 }
复制代码

 

 支持safari+chrome+firefox:

复制代码
public enum WebSocketProtocol
    {
        /*
         * 
         * Request
            GET /WebIM5?uaid=200513807p8912-8de8c7e2-c963-4f67-8aca-8028797efbc1&re=0 HTTP/1.1
            Upgrade: WebSocket
            Connection: Upgrade
            Host: 10.10.150.60:5002
            Origin: https://localhost:444
            Sec-WebSocket-Key1: 3+3 1  8kgV"m 0 8  64u43
            Sec-WebSocket-Key2: 3_7891 6 4 `50 `8
         * 
         * Response
            HTTP/1.1 101 WebSocket Protocol Handshake
            Upgrade: WebSocket
            Connection: Upgrade
            Sec-WebSocket-Origin: https://localhost:444
            Sec-WebSocket-Location: ws://192.168.110.....
            Sec-WebSocket-Protocol: WebIM5
         * 
         *  asdfalskdfa
         * */
        draft_00 = 0,

        /*
         * 
         * Request
            GET /WebIM5?uaid=200513807p8912-2e695e5b-9b46-4511-b59e-28981b4ab327&re=0 HTTP/1.1
            Upgrade: websocket
            Connection: Upgrade
            Host: 10.10.150.60:5002
            Origin: https://localhost:444
            Sec-WebSocket-Key: 1o4Jk9XPGvTX66OxmNMaww==
            Sec-WebSocket-Version: 13
         * 
         * Response
            HTTP/1.1 101 Switching Protocols
            Upgrade: websocket
            Connection: Upgrade
            Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
            Sec-WebSocket-Protocol: WebIM5
         * */
        draft_17 = 17
    }
复制代码

 

其中,safari和chrome都是 :_ws = new WebSocket("ws://ip:port");  但是firefox是:_ws = new window.MozWebSocket("ws://ip:port");

 

浏览器方面:

chrome可以查看WebSocket的访问日志: chrome://net-internals/

你可能感兴趣的:(WebScoket支持safari+chrome+firefox的规范和协议)