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 五.扩 ...
随机推荐
- blog笔录1
(1)虚拟主机 (2)部署 部署完成后刷新页面会看到笑脸,在Home分组下控制器Application/Home/Controller/IndexController.class.php下定义显示 ( ...
- 虚拟机找不到本机vmnet0,vmnet8,无法连接xshell,解决方案
首先出现这个问题肯定是第一次下载虚拟机把之前的注册表覆盖了,网卡找不到,首先卸载VMware 并且将C\ProgramData下的VMware文件夹删除掉 ,下载cceaner,点击注册表清除干净,再 ...
- gson序列化后整形变浮点问题解决方案
字段值是json格式的字符串.我需要将这个字段反序列化为List<Map>形式,但是在反序列化后,id变为了1.0. 百度了很多然并卵,最后改用了阿里的fastjson,没问题.(jack ...
- xmpp之配置Xcode(1)
介绍 ios上的XMPPFramework你能够在Xcode/iPhoneXMPP 目录找到,它只是实现了XMPP的一小部分功能. 下面主要介绍在开发XMPPFramework ios应用之前的配置工 ...
- Luogu P2176 [USACO14FEB]路障Roadblock
解题思路 这是一道最短路题目,不知道大家有没有做过玛丽卡这道题目,如果没做,在做完这道题之后可以去拿个双倍经验哦 先求出一张图中的最短路径,并将其记录下来,我们首先思考:要有增量的前提是新的最短路径比 ...
- UVA-227 Puzzle(模拟)
题目: 题目浏览传送门 题意: 给出一个5*5的方格,里边有一个格子是空的,现在给出一串指令,A->空格向上移动,B->空格向下移动,R->空格向右移动,L->空格向左移动. ...
- 线性DP LIS浅谈
LIS问题 什么是LIS? 百度百科 最长上升子序列(Longest Increasing Subsequence,LIS),在计算机科学上是指一个序列中最长的单调递增的子序列. 怎么求LIS? O( ...
- Linux 服务器 U盘安装(避免U盘启动)以及拔除U盘后无法引导系统
一.U盘制作 首先下载两个文件: · rhel-server-6.3-i386-boot.iso 启动镜像 · rhel-server-6.3-i386-dvd. ...
- vue v-model 的注意点
在使用表单元素 input 的 v-model 指令时,碰到一个问题: 如上图,修改 input 的内容,以便随时显示:但显示时明显慢一步. <template> <div> ...
- 解决windows安装TensorFlow2.0beta版本时ERROR: Cannot uninstall 'wrapt'问题
pip install -U --ignore-installed wrapt enum34 simplejson netaddr 参考:https://bugs.launchpad.net/rall ...