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 五.扩 ...
随机推荐
- Vue指令的概念
指令(Directives) 是带有v- 前缀的特殊属性,指令属性是单一的js表达式. 指令的职责就是表达式的值发生变化时,在DOM中做出相应的回应. 如下例子: 实例 <div id=&quo ...
- JavaScipt30(第六个案例)(主要知识点:给数字加千分号的正则)
承接上文,这是第6个案例: 附上项目链接: https://github.com/wesbos/JavaScript30 这个主要是要实现在给定的json里匹配出搜索框里的city or state, ...
- 浅谈es6 promise
本文是借鉴于ac黄的博客. 接触es6也有几个月了,貌似没有系统的去学习过它,总是用到什么,查查什么.今天就说下es6中的promise对象. 先说说promise解决了什么问题? 写前端的同学都经常 ...
- 【计算几何】二维凸包——Graham's Scan法
凸包 点集Q的凸包(convex hull)是指一个最小凸多边形,满足Q中的点或者在多边形边上或者在其内.右图中由红色线段表示的多边形就是点集Q={p0,p1,...p12}的凸包. 一组平面上的点, ...
- 洛谷——P1850 换教室
P1850 换教室 有 2n 节课程安排在 nn 个时间段上.在第 i个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 $c_i$ 上课,而另一节课程在教室 $d_i$ ...
- 我的第一个随笔——MarkDown的简单用法!
目录 我的第一个笔记 1. 学习简单的markdown语法 1.1 标题 1.2 引用 1.3 倾斜与加粗 1.4无序列表 1.5有序列表 1.6图片和网页 1.7分割线 1.8代码块 1.9结尾 2 ...
- 阿里云安装nodejs
cd进入root目录下: cd /root 下载node.js安装包 wget https://nodejs.org/dist/v8.11.2/node-v8.11.2-linux-x64.tar.x ...
- axios在实际项目中的使用介绍
1.axios本身就封装了各种数据请求的方法 执行 GET 请求 // 为给定 ID 的 user 创建请求 axios.get('/user?ID=12345') .then(function (r ...
- noip模拟赛 梦想
题目描述 LYK做了一个梦. 这个梦是这样的,LYK是一个财主,有一个仆人在为LYK打工. 不幸的是,又到了月末,到了给仆人发工资的时间.但这个仆人很奇怪,它可能想要至少x块钱,并且当LYK凑不出恰好 ...
- Uva122 Trees on the level
Background Trees are fundamental in many branches of computer science. Current state-of-the art para ...