php使用WebSocket详细教程之对接收数据解包及发送数据包装(二)
2021-01-21 03:13
标签:中间设备 sdn 十六进制 大端字节序 end 行数据 辅助 tin ESS 接上篇介绍如何建立连接等基础了解,接下来介绍的是服务器接收到数据的转化,获得真实数据。 (一)WebSocket数据的收发协议 在这里,首先我们需要理解的是1byte(1字节)=8bit(8位)=2位16进制数,在接下来代码中会涉及到。 另外数据的实际长度,会存在三种情况,这里先解释一下,在代码中也会有详细的解释。第一种情况当payload len的长度小于126时,payload len及时实际的数据的长度,第二种情况payload len的值等于126时,payload len其后2byte代表数据的真实长度,第三种情况,也就是等于127时,payload len其后8byte代表数据的真实长度。另外,masking-key其后会紧跟真实数据。如下图。 (二)什么是masking-key 为了避免面这种针对中间设备的攻击,以非HTTP标准的frame作为用户数据的前缀是没有说服力的,因为不太可能彻底发现并检测每个非标准的frame是否能够被非HTTP标准的中间设施识别并略过,也不清楚这些frame数据是否对中间设施的行为产生错误的影响。 对此,WebSocket的防御措施是mask所有从客户端发往服务器的数据,这样恶意脚本(攻击者)就没法获知网络链路上传输的数据是以何种形式呈现的,所以他没法构造可以被中间设施误解为HTTP请求的frame。 pack(format,args+) 函数把数据装入一个二进制字符串。 unpack(format,data) 函数从二进制字符串对数据进行解包。 format这里在其后可跟一个数值或者*,详见php手册,以下例子可以帮助你理解。 由于下篇引用需要,对其进行两个函数进行封装。 https://blog.csdn.net/Vae_sun/article/details/90347802 php使用WebSocket详细教程之对接收数据解包及发送数据包装(二) 标签:中间设备 sdn 十六进制 大端字节序 end 行数据 辅助 tin ESS 原文地址:https://www.cnblogs.com/7qin/p/13299534.html
本篇需要理解的内容:
首先,对于客户端向服务器发送数据,都是以数据帧形式传输,下面给出数据帧格式 1 0 1 2 3
2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
3 +-+-+-+-+-------+-+-------------+-------------------------------+
4 |F|R|R|R| opcode|M| Payload len | Extended payload length |
5 |I|S|S|S| (4) |A| (7) | (16/64) |
6 |N|V|V|V| |S| | (if payload len==126/127) |
7 | |1|2|3| |K| | |
8 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
9 | Extended payload length continued, if payload len == 127 |
10 + - - - - - - - - - - - - - - - +-------------------------------+
11 | |Masking-key, if MASK set to 1 |
12 +-------------------------------+-------------------------------+
13 | Masking-key (continued) | Payload Data |
14 +-------------------------------- - - - - - - - - - - - - - - - +
15 : Payload Data continued ... :
16 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
17 | Payload Data continued ... |
18 +---------------------------------------------------------------+
19
20 具体每一bit的意思
21 FIN 1bit 表示信息的最后一帧
22 RSV 1-3 1bit each 以后备用的 默认都为 0
23 Opcode 4bit 帧类型,稍后细说
24 Mask 1bit 掩码,是否加密数据,默认必须置为1
25 Payload len 7bit 数据的长度
26 Masking-key 1 or 4 bit 掩码
27 Payload data (x + y) bytes 数据
28 Extension data x bytes 扩展数据
29 Application data y bytes 程序数据
WebSocket协议规范:为了避免迷惑网络中介(如代理服务器),以及涉及到安全问题,客户端必须mask所有送给服务器的frame。(三)pack与unpack
(四)数据解包与包装
slen[$key])) {
//获得第二字节,也就是再后两位16进制,第二个字节包含掩码(1bit)+数据长度(7bit)=8bit,也是为2位16进制数
$len=substr($msg[1],2,2);
$len=hexdec($len);//把十六进制的转换为十进制
//这里我们把‘fe’转化为二进制,也就是11111110,第一位为掩码值为1,也就是证明掩码加密
//而其后的1111110,其实也就是126,此时我们就要看向上面介绍的
if(substr($msg[1],2,2)==‘fe‘){
//此时再获取其后的两位得到数据的16进制长度
$len=substr($msg[1],4,4);
$len=hexdec($len);//转化为10进制
print_r("beforemsg:".$msg[1]."\n");
//从数组的第5位字符开始截取新的字符串,即真实长度之后的字符串,由于紧跟其后的为4byte的umask和真实数据
//方便之后统一获得umask,截取该字符串,往下看就知道
$msg[1]=substr($msg[1],4);
print_r("aftermsg:".$msg[1]."\n");
}
//接下来就是payload len为127的情况‘ff’二进制为11111111,与‘fe’同理的理解方式
else if(substr($msg[1],2,2)==‘ff‘){
//很显然,实际数据长度为16进制的8byte*2=16位
//得到16进制的实际数据长度并转化为十进制
$len=substr($msg[1],4,16);
$len=hexdec($len);
print_r("beforemsg:".$msg[1]."\n");
//同理,从数组的第16位字符开始截取新的字符串,之前的为控制位,之后的为数据位的内容
//由于以下设计从第四开始截取umask,所以不能从第20个字符截取,留四个
$msg[1]=substr($msg[1],16);
print_r("aftermsg:".$msg[1]."\n");
}
//这里获取4byte的umask,umask其后紧跟真实的数据
//这里根据不同情况统一处理,可能没仔细看会有点乱,理解不了的可以评论告诉我
//我另写一个不统一获取的
$mask[] = hexdec(substr($msg[1],4,2));
$mask[] = hexdec(substr($msg[1],6,2));
$mask[] = hexdec(substr($msg[1],8,2));
$mask[] = hexdec(substr($msg[1],10,2));
$s = 12;//真实数据在$msg的起始位置
$n=0;//初始n为0
}
//如果到这里就是判断是分片消息的处理了(不懂自己可以去了解下,涉及太多不讲解),需要综合上一个接收的数据处理,
else if($this->slen[$key] > 0){
$len=$this->slen[$key];
$mask=$this->keys[$key];
$n=$this->n[$key];
$s = 0;
}
//这个也顺便说,每次自加2,所以最多到$msg[1]长度减2,强迫详细解释
$e = strlen($msg[1])-2;
for ($i=$s; $i 255 && $len > $dlen+intval($this->sjen[$key])){
$this->keys[$key]=$mask;//mask掩码
$this->slen[$key]=$len;//数据总长度
$this->sjen[$key]=$dlen+intval($this->sjen[$key]);//接收数据长度
$this->sda[$key]=$this->sda[$key].$data;//已接收数据
$this->n[$key]=$n;//更新n
//返回false,例如想把接收的数据发给谁,由于数据不完整,并不能发送,所以要跳过接收消息要处理的程序,等待数据完整再发送。
return false;
}
//在这里就意味着消息已经完整
else{
//销毁、释放辅助变量
unset($this->keys[$key],$this->slen[$key],$this->sjen[$key],$this->n[$key]);
//取出完整的数据
$data=$this->sda[$key].$data;
//然后在释放辅助记录完整数据的变量。
unset($this->sda[$key]);
//返回完整的数据
return $data;
}
}
//与uncode相对,理解解码之后,code就会容易理解多了,
function code($msg){
$frame = array();
//81开头固定
$frame[0] = ‘81‘;
$len = strlen($msg);
//frame[1]构造数据长度信息
//长度小于126时,就构造一个16进制作为字符串长度即payload len
if($len ord_hex($msg);
//将frame数组连接成字符串
$data = implode(‘‘,$frame);
//pack()函数把数据装入一个二进制字符串,"H*"将数据按大端字节序的16进制格式包装。
return pack("H*", $data);
}
function ord_hex($data) {
$msg = ‘‘;
$l = strlen($data);
for ($i= 0; $i
文章标题:php使用WebSocket详细教程之对接收数据解包及发送数据包装(二)
文章链接:http://soscw.com/index.php/essay/44828.html