网址: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: (.*)\r\n/",$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\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Accept: " . $hash_data . "\r\n" .
"\r\n";
?>
  • 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: (.*)\r\n/",$req,$match)){ $key1=$match[1]; }
if(preg_match("/Sec-WebSocket-Key2: (.*)\r\n/",$req,$match)){ $key2=$match[1]; }
?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
<?php
//获取请求正文内容
$arr = explode("\r\n", $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\r\n" .
"Upgrade: WebSocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Origin: $origin\r\n".
"Sec-WebSocket-Location: ws://$host/\r\n".
"\r\n" . $hash_data;
?>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

握手成功!

WebSocket握手总结的更多相关文章

  1. 探讨Netty获取并检查Websocket握手请求的两种方式

    在使用Netty开发Websocket服务时,通常需要解析来自客户端请求的URL.Headers等等相关内容,并做相关检查或处理.本文将讨论两种实现方法. 方法一:基于HandshakeComplet ...

  2. 使用 Sa-Token 解决 WebSocket 握手身份认证

    前言 相比于 Http 的单项通信方式,WebSocket 可以从服务器向浏览器主动推送消息,这一特性可以帮助我们完成诸如 订单消息推送.IM实时聊天 等一些特定业务. 然而 WebSocket 本身 ...

  3. node.js 使用 net 模块模拟 websocket 握手,进行数据传递。

    websocket 是一种让浏览器与服务器之间建立持久的连接,并能进行双向数据传输的一种协议. websocket 属性应用层协议,基于tcp传输协议,并复用http的握手通道. 一.如何进行webs ...

  4. flask POOL,websocket握手

    一.POOL Pool就是为了多线程访问数据库,减少数据库压力 回顾pymysql import pymysql #建立连接 mysql_conn = pymysql.connect(host=&qu ...

  5. python模拟websocket握手过程中计算sec-websocket-accept

    背景 以前,很多网站使用轮询实现推送技术.轮询是在特定的的时间间隔(比如1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给浏览器.轮询的缺点很明显,浏览器需要不断的向服 ...

  6. Workman websocket 握手连接

    默认的是TCP连接方式,如果需要WebSocket,则需要更改Gateway方式, 服务端协议要和客户端协议一致才能通讯.客户端是websocket协议,服务端也要设置成websocket协议.默认为 ...

  7. websocket 实现单聊群聊 以及 握手原理+加密方式

    WebSocket 开始代码 服务端 群聊 # type:WebSocket 给变量标注类型 # websocket web + socket from geventwebsocket.server ...

  8. Flask-session,WTForms,POOL,Websocket通讯原理 -握手,加密解密过程

    1.Flask-session Flask中的session 需要执行 session_interface - open_session存储到redis中,存的key:session:d3f07db2 ...

  9. 借助Nodejs探究WebSocket

    文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...

随机推荐

  1. linux ssh 利用scp传输文件

    使用方式如下: 1.上传本地文件到服务器 scp /path/filename username@servername:/path/ 例如scp /var/www/test.php root@192. ...

  2. you have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'varchar(255), sort integer not null

    you have an error in your SQL syntax; check the manual that corresponds to your MySQL server version ...

  3. 个人Linux(ubuntu)使用记录——远程访问linux

    说明:记录自己的linux使用过程,并不打算把它当作一个教程,仅仅只是记录下自己使用过程中的一些命令,配置等东西,这样方便自己查阅,也就不用到处去网上搜索了,所以文章毫无章法可言,甚至会记录得很乱. ...

  4. The King’s Ups and Downs(HDU 4489,动态规划递推,组合数,国王的游戏)

    题意: 给一个数字n,让1到n的所有数都以波浪形排序,即任意两个相邻的数都是一高一低或者一低一高 比如:1324   4231,再比如4213就是错的,因为4高,2低,接下来1就应该比2高,但是它没有 ...

  5. mac 中查看监听程序

    sudo lsof -nP -iTCP -sTCP:LISTEN | grep mysql

  6. STM32单片机串口一键下载电路与操作方法详解

    STM32三种启动模式对应的存储介质均是芯片内置的,它们是:1)用户闪存 = 芯片内置的Flash.2)SRAM = 芯片内置的RAM区,就是内存啦.3)系统存储器 = 芯片内部一块特定的区域,芯片出 ...

  7. 2018百度之星资格赛T2 子串查询

    [题解] 很容易想到暴力做法:对于每个询问暴力查找区间内的最小字母,统计其出现次数.效率O(N^2),无法通过全部数据. 我们可以换一个思路,设f[i][j]为第i个字母(字母‘A'到’Z'分别对应0 ...

  8. [bzoj1925][Sdoi2010][地精部落] (序列动态规划)

    Description 传说很久以前,大地上居住着一种神秘的生物:地精. 地精喜欢住在连绵不绝的山脉中.具体地说,一座长度为 N 的山脉 H可分 为从左到右的 N 段,每段有一个独一无二的高度 Hi, ...

  9. MaxScale初探

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://mrcto.blog.51cto.com/1923168/1437287 内容预览 ...

  10. noip模拟赛 蒜头君的坐骑

    分析:标准的棋盘dp问题. 如果没有技能,那么就很好做了,相当于传纸条的做法.有了技能的限制,我们就要加上一维表示用了多少次技能,这个时候转移就要用到dfs了,而且不能用填表法,要用刷表法,从当前位置 ...