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: (.*)\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握手总结的更多相关文章
- 探讨Netty获取并检查Websocket握手请求的两种方式
在使用Netty开发Websocket服务时,通常需要解析来自客户端请求的URL.Headers等等相关内容,并做相关检查或处理.本文将讨论两种实现方法. 方法一:基于HandshakeComplet ...
- 使用 Sa-Token 解决 WebSocket 握手身份认证
前言 相比于 Http 的单项通信方式,WebSocket 可以从服务器向浏览器主动推送消息,这一特性可以帮助我们完成诸如 订单消息推送.IM实时聊天 等一些特定业务. 然而 WebSocket 本身 ...
- node.js 使用 net 模块模拟 websocket 握手,进行数据传递。
websocket 是一种让浏览器与服务器之间建立持久的连接,并能进行双向数据传输的一种协议. websocket 属性应用层协议,基于tcp传输协议,并复用http的握手通道. 一.如何进行webs ...
- flask POOL,websocket握手
一.POOL Pool就是为了多线程访问数据库,减少数据库压力 回顾pymysql import pymysql #建立连接 mysql_conn = pymysql.connect(host=&qu ...
- python模拟websocket握手过程中计算sec-websocket-accept
背景 以前,很多网站使用轮询实现推送技术.轮询是在特定的的时间间隔(比如1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给浏览器.轮询的缺点很明显,浏览器需要不断的向服 ...
- Workman websocket 握手连接
默认的是TCP连接方式,如果需要WebSocket,则需要更改Gateway方式, 服务端协议要和客户端协议一致才能通讯.客户端是websocket协议,服务端也要设置成websocket协议.默认为 ...
- websocket 实现单聊群聊 以及 握手原理+加密方式
WebSocket 开始代码 服务端 群聊 # type:WebSocket 给变量标注类型 # websocket web + socket from geventwebsocket.server ...
- Flask-session,WTForms,POOL,Websocket通讯原理 -握手,加密解密过程
1.Flask-session Flask中的session 需要执行 session_interface - open_session存储到redis中,存的key:session:d3f07db2 ...
- 借助Nodejs探究WebSocket
文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...
随机推荐
- java.net.MalformedURLException: no protocol: www.baidu.com
URL url = new URL("www.baidu.com");改为 URL url = new URL("http://www.baidu.com");
- 如何创建TWaver 3D的轮廓选中效果
在一般的游戏中,物体的选中效果会是这样: TWaver 3D中,物体的默认的选中效果一般都是一个方方正正的外框.在HTML5的Mono版本中,TWaver提供了轮廓线样式的选中效果. 通过如下代码把几 ...
- cc.Node—场景树
对于cc.Node我分了四个模块学习: 1.场景树,2.事件响应,3.坐标系统,4.Action的使用:在此记录经验分享给大家. 场景树 1: creator是由一个一个的游戏场景组成,通过代码逻辑来 ...
- localStorage前端存储数据
<!DOCTYPE html> <html> <head> <title>localStorage演示</title> <meta c ...
- Linux之修改主机名(永久生效)
Linux系统安装好后,都会有默认的主机名,这里以CentOS系统为例,默认的主机名为localhost.localdomain,为了便于使用,我们常常需要修改主机名,下面演示的是永久更改主机名的方法 ...
- page对象的使用及常见方法
page对象的使用及常见方法 制作人:全心全意 page对象代表JSP本身,只有在JSP页面内才是合法的.page对象本质上是包含当前Servlet接口引用的变量,可以看作是this关键字的别名. p ...
- 在此计算机中仅有部分visual studio2010产品已升级到SP1,只有全部升级,产品才能正常运行
先说废话: 本人机子刚装系统Win10 专业版 1709 开始安装vs2010的时候中途报错了,有一个什么驱动不兼容,被我给关闭了,继续安装完,然后找不到vs的启动快捷方式,开始里面没有,于是我开始修 ...
- Hihocoder 1329(splay)
Problem 平衡树 Splay 题目大意 维护一个数列,支持三种操作. 操作1:添加一个数x. 操作2:询问不超过x的最大的数. 操作三:删除大小在区间[a,b]内的数. 解题分析 和上一题相比, ...
- Weakness and Poorness CodeForces - 578C 三分搜索 (精度!)
You are given a sequence of n integers a1, a2, ..., an. Determine a real number x such that the weak ...
- Mysql 使用delete drop truncate 删除数据时受外键约束影响解决方案
先禁用数据库的外键约束: set foreign_key_checks=0; 进行删除操作 delete.drop.truncate 恢复数据库外键约束: set foreign_key_checks ...