zoukankan      html  css  js  c++  java
  • websocket终成标准

    WebSocket是用于浏览器或其他客户端,建立与web服务器的双向、可靠通信渠道的协议。与其他方法相比的最大好处是,不需要使用多个XML HTTP请求来完成,或者是必须让一个正常的HTTP链接尽可能长时间的保持Open.
       
        历时11年,WebSocket终于被批准成为IETF的建议标准:RFC6455.其前身是WHATWG (Web Hypertext Application Technology Working Group)的工作。而Web Socket的API,是W3C的工作。
       
        WebSocket可以只打开一个到服务器的链接,并且在此链接上交换信息。其优势在于减少了传统方法的复杂性,提高了可靠性和降低了浏览器和客户端之间的负载。这样做的一个重要原因是, 很多防火墙屏蔽80以外的端口,迫使越来越多的应用迁移到HTTP上来了。
       
        目前多数浏览器已经支持WebSocket,如iOS Safari 4.2, Opera Mobile 11,chrome 14, Firefox5等,但IE9不支持。

        如果用websocket 的话 也许 有的server链接不上,可能是因为11年的websocket草案的变迁中,有的浏览器支持的是旧版本的websocket,比如iPhone4上的safari使用的WebSocket是旧版的握手协议,那么就要使用就的握手协议来制做服务器

       

    起初,Mozilla基金会Mozilla Firefox会在4版本支持websocket。Opera软件公司方面在Opera 10.7和11.0的预览版本中也支持了websocket。然而,基于安全因素的考虑,两家宣布将暂时移除该功能。FireFox预计于版本6重新实现WebSockets RFC Version -07 ,但此版本实现并不向后兼容,故旧版本的服务器实现软件有可能无法顺利运行。版本6之中的WebSocket功能将会默认打开。

    如今只有Safari支持旧版本的协议,Chrome和Firefox最新版都已升级至Hybi-10(协议地址)。因此,我们再来看一下WebSocket新版协议Hybi-10。这次协议变更非常大,主要集中在握手协议和数据传输的格式上。下面我们来详细介绍一下。

    握手协议

    我们先来看一下大致的区别:

    1. 最老的websocket草案标准中是没有安全key,草案7.5、7.6中有两个安全key,而现在的草案10中只有一个安全key,即将 7.5、7.6中http头中的”Sec-WebSocket-Key1″与”Sec-WebSocket-Key2″合并为了一个”Sec- WebSocket-Key”
    2. 把http头中Upgrade的值由”WebSocket”修改为了”websocket”;http头中的”-Origin”修改为了”Sec-WebSocket-Origin”;
    3. 增加了http头”Sec-WebSocket-Accept”,用来返回原来草案7.5、7.6服务器返回给客户端的握手验证,原来是以内容的形式返回,现在是放到了http头中;另外服务器返回客户端的验证方式也有变化。

    服务器生成验证的方式变化较大,我们来做一介绍。

    旧版:

    1 GET / HTTP/1.1
    2 Upgrade: WebSocket
    3 Connection: Upgrade
    4 Host: 127.0.0.1:1337
    5 Origin: http://127.0.0.1:8000
    6 Cookie: sessionid=xxxx; calView=day; dayCurrentDate=1314288000000
    7 Sec-WebSocket-Key1: cV`p1* 42#7  ^9}_ 647  08{
    8 Sec-WebSocket-Key2: O8 415 8x37R A8   4
    9 ;“######

    旧版生成Token的方法如下:

    取出Sec-WebSocket-Key1中的所有数字字符形成一个数值,这里是1427964708,然后除以Key1中的空格数目,得到一个数 值,保留该数值整数位,得到数值N1;对Sec-WebSocket-Key2采取同样的算法,得到第二个整数N2;把N1和N2按照Big- Endian字符序列连接起来,然后再与另外一个Key3连接,得到一个原始序列ser_key。Key3是指在握手请求最后,有一个8字节的奇怪的字符 串“;”######”,这个就是Key3。然后对ser_key进行一次md5运算得出一个16字节长的digest,这就是老版本协议需要的 token,然后将这个token附在握手消息的最后发送回Client,即可完成握手。

    新版:

    1 GET / HTTP/1.1
    2 Upgrade: websocket
    3 Connection: Upgrade
    4 Host: 127.0.0.1:1337
    5 Sec-WebSocket-Origin: http://127.0.0.1:8000
    6 Sec-WebSocket-Key: erWJbDVAlYnHvHNulgrW8Q==
    7 Sec-WebSocket-Version: 8
    8 Cookie: csrftoken=xxxxxx; sessionid=xxxxx

    新版生成Token的方法如下:

    首先服务器将key(长度24)截取出来,如4tAjitqO9So2Wu8lkrsq3w==,用它和自定义的一个字符串(长度 36)258EAFA5-E914-47DA-95CA-C5AB0DC85B11连接起来,然后把这一字符串进行SHA-1算法加密,得到长度为20字 节的二进制数据,再将这些数据经过Base64编码,最终得到服务端的密钥,也就是ser_key。服务器将ser_key附在返回值Sec- WebSocket-Accept后,至此握手成功。

    数据报文格式

    旧版协议比较简单,仅仅是在原始数据前加了个’\x00′,在最后面加了个’\xFF’,即假如Client发送一个字符串’test’,实际上WebSocket Server收到的数据是:’x00test\xFF’,所以只需要剥离掉首尾那两个字符就可以了。

    新版的协议对这部分规定比较复杂,以下是其格式标准:(下图在Firefox可能会出现错乱,请换用Chrome)

    FIN:1位,用来表明这是一个消息的最后的消息片断,当然第一个消息片断也可能是最后的一个消息片断;

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

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

    • %x0 表示连续消息片断
    • %x1 表示文本消息片断
    • %x2 表未二进制消息片断
    • %x3-7 为将来的非控制消息片断保留的操作码
    • %x8 表示连接关闭
    • %x9 表示心跳检查的ping
    • %xA 表示心跳检查的pong
    • %xB-F 为将来的控制消息片断的保留操作码

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

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

    Masking-key:0或4个字节,客户端发送给服务端的数据,都是通过内嵌的一个32位值作为掩码的;掩码键只有在掩码位设置为1的时候存在。
    Payload data:  (x+y)位,负载数据为扩展数据及应用数据长度之和。
    Extension data:x位,如果客户端与服务端之间没有特殊约定,那么扩展数据的长度始终为0,任何的扩展都必须指定扩展数据的长度,或者长度的计算方式,以及在握手时如何确定正确的握手方式。如果存在扩展数据,则扩展数据就会包括在负载数据的长度之内。
    Application data:y位,任意的应用数据,放在扩展数据之后,应用数据的长度=负载数据的长度-扩展数据的长度。

    以下是Python实现的解码和编码,兼容新旧版协议,仅供参考:

    01 def send_data(self, raw_str):
    02     if self.sockets[self]['new_version']:
    03         back_str = []
    04         back_str.append(\x81)
    05         data_length = len(raw_str)
    06
    07         if data_length <= 125:
    08             back_str.append(chr(data_length))
    09         else:
    10             back_str.append(chr(126))
    11             back_str.append(chr(data_length >> 8))
    12             back_str.append(chr(data_length & 0xFF))
    13
    14         back_str = “”.join(back_str) + raw_str
    15         self.transport.write(back_str)
    16     else:
    17         back_str = \x00%s\xFF % (raw_str)
    18         self.transport.write(back_str)
    19
    20 def parse_recv_data(self, msg):
    21     raw_str =
    22
    23     if self.sockets[self]['new_version']:
    24         code_length = ord(msg[1]) & 127
    25
    26         if code_length == 126:
    27             masks = msg[4:8]
    28             data = msg[8:]
    29         elif code_length == 127:
    30             masks = msg[10:14]
    31             data = msg[14:]
    32         else:
    33             masks = msg[2:6]
    34             data = msg[6:]
    35
    36         i = 0
    37         for d in data:
    38             raw_str += chr(ord(d) ^ ord(masks[i%4]))
    39             i += 1
    40     else:
    41         raw_str = msg.split(\xFF)[0][1:]
    42
    43     return raw_str

    PS: 在FireFox6的版本里,WebSocket 被更名为 MozWebSocket,但是该 class 的成员与用法皆与 WebSocket 相同。

  • 相关阅读:
    Python 之 Django框架( Cookie和Session、Django中间件、AJAX、Django序列化)
    SAP 公司间开票 报错 :0080264464 000000 销售机构 未定义
    C++虚函数、纯虚函数,继承及多态、友元
    postgre 用户权限管理
    mysql 主从搭建
    vue 配置开发线上环境
    基于Ant Design UI框架的React项目
    postgresql数据库报“connections on Unix domain socket "/tmp/.s.PGSQL.5432"?”
    postgres 连接数查看与设置
    修改postgresql密码
  • 原文地址:https://www.cnblogs.com/litao229/p/2677292.html
Copyright © 2011-2022 走看看