• WebSocket握手总结


    网址:http://blog.csdn.net/edwingu/article/details/44040961

    WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。

    原理

    以前网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每隔1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求,然而HTTP request 的header是非常长的,里面包含的有用数据可能只是一个很小的值,这样会占用很多的带宽。
    在 WebSocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
    

    握手协议

    在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。 
    该方案处在草案阶段,目前在使用的有两个版本,一个是以chrome为首的使用的version 13(目前最新),该版本出现在RFC6455中。另一个是以safari(包括桌面和移动版本)为首的使用的draft-ietf-hybi版。 
    chrome版–新版 
    safari版–旧版

    以下分别介绍两个版本的握手方法

    Chrome版

    客户端请求web socket连接时,会向服务器端发送握手请求

    客户端请求:
    var ws = new WebSocket('ws://192.168.0.10:8080');
    • 1
    • 2
    • 1
    • 2

    请求内容大致如下:

    GET / HTTP/1.1
    Host: 192.168.0.10:8080
    Connection: Upgrade
    Pragma: no-cache
    Cache-Control: no-cache
    Upgrade: websocket
    Origin: null
    Sec-WebSocket-Version: 13
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
    Accept-Encoding: gzip, deflate, sdch
    Accept-Language: zh-CN,zh;q=0.8
    Sec-WebSocket-Key: VR+OReqwhymoQ21dBtoIMQ==
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    请求包说明: 
    * 必须是有效的http request 格式; 
    * HTTP request method 必须是GET,协议应不小于1.1 如: Get / HTTP/1.1; 
    * 必须包括Upgrade头域,并且其值为”websocket”; 
    * 必须包括”Connection” 头域,并且其值为”Upgrade”; 
    * 必须包括”Sec-WebSocket-Key”头域,其值采用base64编码的随机16字节长的字符序列; 
    * 如果请求来自浏览器客户端,还必须包括Origin头域 。 该头域用于防止未授权的跨域脚本攻击,服务器可以从Origin决定是否接受该WebSocket连接; 
    * 必须包括”Sec-webSocket-Version” 头域,当前值必须是13; 
    * 可能包括”Sec-WebSocket-Protocol”,表示client(应用程序)支持的协议列表,server选择一个或者没有可接受的协议响应之; 
    * 可能包括”Sec-WebSocket-Extensions”, 协议扩展, 某类协议可能支持多个扩展,通过它可以实现协议增强; 
    * 可能包括任意其他域,如cookie.

    服务器端响应如下:

    HTTP/1.1 101 Web Socket Protocol Handshake
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: Y+Te7S7wQJC0FwXumEdGbv9/Mek=
    • 1
    • 2
    • 3
    • 4
    • 1
    • 2
    • 3
    • 4

    应答包说明: 
    *必须包括Upgrade头域,并且其值为”websocket”; 
    *必须包括Connection头域,并且其值为”Upgrade”; 
    *必须包括Sec-WebSocket-Accept头域,其值是将请求包“Sec-WebSocket-Key”的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码,就是“Sec-WebSocket-Accept”的值; 
    *应答包中冒号后面有一个空格; 
    *最后需要两个空行作为应答包结束。

    <?php
    //获取请求包中Sec-WebSocket-Key的值
    //$req为请求包内容
    if(preg_match("/Sec-WebSocket-Key: (.*)
    /",$req,$match)){ $key=$match[1]; }
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5
    <?php
        //计算Sec-WebSocket-Accept值算法
        private function websocket_accept_key($strkey){
            $strkey .= "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
            $hash_data = base64_encode(sha1($strkey,true));
            return $hash_data;
        }
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    <?php
    //组装应答包代码
    $hash_data = $this->websocket_accept_key($strkey);
    $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake
    " .
        "Upgrade: websocket
    " .
        "Connection: Upgrade
    " .
        "Sec-WebSocket-Accept: " . $hash_data . "
    " .
        "
    ";
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    握手成功!

    Safari版

    请求内容大致如下:

    GET / HTTP/1.1
    Upgrade: WebSocket
    Connection: Upgrade
    Host: 192.168.0.10:8080
    Origin: file://
    Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
    Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
    
    ^n:ds[4U
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    请求包说明: 
    *必须包括”Sec-WebSocket-Key1”头域,由可见字符组成; 
    *必须包括”Sec-WebSocket-Key2”头域,由可见字符组成; 
    *在请求包的最后,会有一行请求正文,它不属于任何头域,我们可以理解为”Sec-WebSocket-Key3”,因为它需要参与到计算应答体; 
    *请求正文的上一行是一行空行; 
    *请求正文大部分情况下是非可见字符,俗称乱码,但这不影响后面的计算。

    服务器端响应如下:

    HTTP/1.1 101 WebSocket Protocol Handshake
    Upgrade: WebSocket
    Connection: Upgrade
    Sec-WebSocket-Origin: file://
    Sec-WebSocket-Location: ws://192.168.0.10:8080/
    
    8jKS'y:G*Co,Wxa-
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    应答包说明: 
    *必须包括”Upgrade”头域,其值为WebSocket; 
    *必须包括”Connection”头域,其值为Upgrade; 
    *必须包括”Sec-WebSocket-Origin”头域; 
    *必须包括”Sec-WebSocket-Location”头域; 
    *应答正文的上一行是一行空行; 
    *应答正文后面没有任何结束符或者换行符。

    <?php
    //获取请求包中Sec-WebSocket-Key1和Sec-WebSocket-Key2值
    //$req为请求包内容
    if(preg_match("/Sec-WebSocket-Key1: (.*)
    /",$req,$match)){ $key1=$match[1]; }
    if(preg_match("/Sec-WebSocket-Key2: (.*)
    /",$req,$match)){ $key2=$match[1]; }
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    <?php
    //获取请求正文内容
    $arr = explode("
    ", $req);
    $body = end($arr);
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5
    <?php
     /*
        计算应答正文内容
        1.把Sec-WebSocket-Key1中的数字从左到右提取出来,上面的例子是:4146546015;
        2.把Sec-WebSocket-Key2中的数字从左到右提取出来,上面的例子是:1299853100;
        3.计算Sec-WebSocket-Key1中的空格数,上面的例子是:5(冒号后面的空格不算);
        4.计算Sec-WebSocket-Key2中的空格数,上面的例子是:5(冒号后面的空格不算);
        5.将提取出来的数字除以空格数,去整,分别得到829309203 和 259970620;
        6.将上一步计算得到的数字分别以Big-Endian的方式打包,拼接,然后再与请求正文的8个字节拼接,计算其MD5值。
     */
     public function websocket_accept_key_76($key1, $key2, $body){
    
            $tmp = array();
            $kv1 = 0;
            $kv2 = 0;
            $c1 = 0;
            $c2 = 0;
    
            $key1Len = strlen($key1);
            $key2Len = strlen($key2);
    
            for($i = 0; $i < $key1Len; $i++){
                if($key1[$i]>='0' && $key1[$i]<='9') $kv1 = $kv1*10+($key1[$i]-'0');
                else if($key1[$i]==' ') $c1++;
            }
    
            for($i = 0; $i < $key2Len; $i++){
                if($key2[$i]>='0' && $key2[$i]<='9') $kv2 = $kv2*10+($key2[$i]-'0');
                else if($key2[$i]==' ') $c2++;
            }
    
            $kv1 = $kv1/$c1;
            $kv2 = $kv2/$c2;
    
            $key1_sec = pack("N",$kv1);
            $key2_sec = pack("N",$kv2);
    
            $hash_data = md5($key1_sec.$key2_sec.$body,true);
            return $hash_data;
        }
    
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    获得的这个就是应答体正文,通常是非可见字符–“乱码”。 
    然后就是返回应答包给浏览器了

    <?php
    //组装应答包代码
    $hash_data = $this->websocket_accept_key_76($strkey);
    $upgrade = ""HTTP/1.1 101 WebSocket Protocol Handshake
    
    " .
                    "Upgrade: WebSocket
    
    " .
                    "Connection: Upgrade
    
    " .
                    "Sec-WebSocket-Origin: $origin
    
    ".
                    "Sec-WebSocket-Location: ws://$host/
    ".
                    "
    " . $hash_data;
    ?>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    握手成功!

  • 相关阅读:
    比服务器的问题
    android studio adb.exe已停止工作(全面成功版 进程的查询和开启)
    安卓学习123
    java eclipse 安卓环境配置
    host访问goole
    Android studio自带的sample例子
    马士兵2string buffuer
    tomcat内存溢出,设置
    spring mvc 学习笔记【1】---前言
    dbutils报错:com.microsoft.sqlserver.jdbc.SQLServerException: 无法识别元数据的表
  • 原文地址:https://www.cnblogs.com/zxtceq/p/7278743.html
Copyright © 2020-2023  润新知