参考文章websocketRFCgithub中文翻译WebsocketRFC文档workermanwebsocket协议实现协议组成该协议由一个开放的握手,接着是基本的消息框架和分层的TCP组成。所要解决的问题是基于浏览器的机制,实现客户端和服务端的双向通信。协议概述来自客户端握手GET/chatHTTP/1.1Host:server.example.comUpgrade:websocketConnection:UpgradeSec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==Origin:http://example.comSec-WebSocket-Protocol:chat,superchatSec-WebSocket-Version:13HandshakefromtheserverHTTP/1.1101SwitchingProtocolsUpgrade:websocketConnection:UpgradeSec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=//Optionalheader,表示允许的客户端Sec-WebSocket-Protocol:Abovechat,headerorderdoes没关系。一旦客户端和服务器都发送了握手信号,如果握手成功,则数据传输部分开始。这是一个供双方通信的通道,彼此独立,可以随意发送数据。服务器的响应不是随机的,你需要遵循一定的规则。请参考RFC文档第6/7页:获取客户端请求的Sec-Weboscket-Key字段的值,去除尾部空白字符并拼接全局唯一标识符258EAFA5-E914-47DA-95CA-C5AB0DC85B11sha1加密(短格式)base64加密PHP程序说明:$client_key='dGhlIHNhbXBsZSBub25jZQ==';$client_key=trim($client_key);$guid='258EAFA5-E914-47DA-95CA-C5AB0DC8$5B11';键=$client_key。$引导;$key=sha1($key,true);$key=base64_encode($key);从上面的结果中得到的值就是服务端返回给客户端握手的Sec-Websocket-Acceptheader字段值。Closelink收到一个0x8的控制帧后,可能立即断开连接,也可能收到后断开剩余的数据。可以有消息体,说明消息产生的原因,可以记录为日志。应用程序在发送关闭帧后不得发送更多数据帧。如果一个端点收到一个关闭帧并且之前没有发送过一个关闭帧,它必须发送一个关闭帧。端点收到关闭帧后,可以延迟响应关闭帧,继续发送或接收数据帧,但不保证已经发送关闭帧的端点继续处理数据。发送和接收关闭帧的端点被认为已经关闭了websocket连接,并且必须关闭底层TCP连接。设计概念基于帧而不是流/文本或二进制帧。该链接要求握手必须是对客户端的有效HTTP请求。请求的方法必须是GET,HTTP版本必须是1.1。请求的REQUEST-URI必须符合文档要求(详见第13页)请求必须包含Host头请求必须包含Upgrade:websocket头,值必须是websocket请求必须包含Connection:Upgradeheader,取值必须为Upgrade请求必须包含Sec-WebSocket-Keyheader请求必须包含Sec-WebSocket-Version:13header,取值必须为13请求必须包含Originheader请求可能包含Sec-WebSocket-Protocolheader,指定子协议请求可能包含Sec-WebSocket-Extensions,指定协议扩展请求可能包含其他字段,如cookies等不满足上述要求的服务器响应,客户端会断开链接。如果响应不包含Sec-WebSocket-Protocol中指定的子协议,客户端将断开连接。如果响应HTTP/1.1101SwitchingProtocols状态码不是101,客户端Disconnect为服务器端要求如果请求是HTTP/1.1或更高版本的GET请求,并且包含REQUEST-URI,应该根据文件要求。Host字段Upgrade头字段的值必须不区分大小写websocketSec-WebSocket-keyd解码时,长度为16ByteSec-WebSocket-Version值必须为13Host如果不包含,链接不应被解释为浏览器发起的behaviorSec-WebSocket-Protocol列出了客户端请求的子协议,service端要按优先顺序排列,响应可选的其他字段满足要求(例如:403)Sec-WebSocket-Key值为base64加密值,服务端不需要解码,仅用于创建服务端握手。验证Sec-WebSocket-Version值,如果不是13,则返回相应的错误码(例如:HTTP/1.1426UpgradeRequired)resourcenameverificationsub-protocolverificationextensionsverification如果上述验证通过,服务器接受该链接。那么响应必须满足以下要求。详见第23页:Required,状态行HTTP/1.1101SwitchingProtocolsrequired,protocolupgradeheaderUpgrade:websocket需要,headerfield表示连接升级Connection:Upgraderequired,Sec-WebSocket-Acceptheaderfield,请参考协议概述部分的详细信息响应!标题的顺序无关紧要!关键是要注意换行符!严格控制数量!):HTTP/1.1101SwitchingProtocols\r\nConnection:Upgrade\r\nUpgrade:websocket\r\nSec-WebSocket-Accept:3nlEzv+LqVBYnTHclAqtk62uOTQ=\r\n//下面的头字段是一个optionalfieldSec-WebSocket-Protocols:chat\r\n\r\n基本框架协议组位的数据传输部分!!由于数据是比特级封装的,如果直接取出来,得到的是处理后的数据,需要解密下图是传输数据格式:012301234567890123456789012345678901+-+-+-+-+--------+-+------------+----------------------------+|F|R|R|R|操作码|M|有效载荷长度|扩展有效载荷长度||我|S|S|S|(4)|一个|(7)|(16/64)||N|V|V|V||S||(如果负载len==126/127)|||1|2|3||K|||+-+-+-+-+------+-+------------+---------------+|扩展有效负载长度继续,如果有效负载len==127|+--------------+--------------------------------+||屏蔽密钥,如果MASK设置为1|+----------------------------+------------------------------+|掩码键(续)|有效载荷数据|+-----------------------------------------------+:有效载荷数据续...:+-------------------------------+|有效负载数据继续...|+---------------------------------------------------------------+1。特殊术语含义介绍1bit,FIN各1bit,RSV1,RSV2,RSV34bit,opcode(下面ABNF定义)%x0连续帧%x1文本帧%x2二进制帧%x3-%x7保留帧%x8连接关闭%x9ping%xApong%xB-FReservedcontrolframes以上表示均为16进制值1bit,maskclient发送给server的数据需要设置为1即数据被mask7bit,7+16位,7+64位。Payloadlength的具体范围可以参考RFC文档(第31页)Playloadlength=ExtendedPayloadlength+ApplicationPayloadlengthPayloadlength=extendeddataLength+applicationdatalength扩展数据长度可能为0,所以当扩展datalength=0,payloadlength=applicationlengthpayload数据的长度单位为Byte0/4字节,masking-keyclient发送给server端的数据进行masking,长度为32bit。服务端发送给客户端的数据没有被屏蔽,长度为0bitx+yByte,PayloadDataxByte,ExtensionDatayByte,ApplicationDataApplicationdata2.看懂图中显示的是按照websocket协议传输的数据.由于数据是通过websocket协议处理的,无法直接获取有效数据。如果要获取有效数据,需要遵循websocket协议。读。图中从左到右,从高到低排列。什么是低和高??这就像一个十进制数。如果有这样的描述:3表示个位,2表示十位,1表示百位。这个数字是多少??答案:123、这个很容易理解,个位、十位、百位表示排列顺序;同样,在编程领域,排列顺序也是从低到高描述的!但是,个位、十位、百位描述的是十进制的排列顺序,而低位和高位描述的是二进制的排列顺序,具体描述为第0位、第1位、第2位……等.(当前例子中的排列顺序是从低位到高位),下面是图片说明:理解了低位和高位之后,上图描述的数据排列顺序就清楚了。众所周知,位(bit)是内存中最小的存储单元,只能存储0和1两个值。所以如果要获取和设置某个位的值,需要执行bit操作。由于是对位进行运算,所以图中描述的内容是在补码的基础上进行的。客户端发送给服务器的数据被屏蔽了!需要分析,分析数据流向://根据websocket规范,分析客户端函数的加密数据decode(string$buffer){//buffer[0]得到第一个字节,8bit//对比那个图片,表示fin+rsv1+rsv2+rsv3+opcode//之所以转换为ASCII码值//是为了保证位运算的结果是正确的!//php的位操作详见:https://note.youdao.com/share/?id=927bfc2f40a8d62f4c9165de30a41e75&type=note#///这里简单说明一下//下面的代码会有$这样的代码first_byte>>7//在php中,<<>>会将操作数视为一个整数(int)//所以如果它没有被转换为ascii值,那么过程将是//(int)$buffer[0]>>7//结果是错误的!!//ord((int)$buffer[0])!==ord($buffer[0])就是最好的证明//因为ascii值不同,所以二进制值(严格来说,我觉得应该说是be:Complementcode)也不同//这违反了websocket规定的协议//会导致解析错误$first_byte=ord($buffer[0]);//buffer[1]得到第二个字节,8bit//和那个图比较,就是mask+payloadlen$second_byte=ord($buffer[1]);//获取左边的第一个值$fin=$first_byte>>7;//对比那张图,如果要得到payloadlen表示的值//需要将bit7设置为0//因为bit7代表mask,bits0-6代表paylaodlen的补码//所以要得到payloadlen的值//01111111=>127$payload_len=$second_byte&127;//客户端发送给服务端的数据被屏蔽了//所以需要得到maskkey+被屏蔽的clientData//得到mask-key+payloaddataif($payload_len===127){//Ifpayloadlen=127byte//payloadlen本身占7bit//扩展payloadlenght占64bit$mask_key=substr($buffer,10,4);$encoded_data=substr($buffer,14);}elseif($payload_len===126){//如果payloadlen=126byte//payload长度本身占7bit//扩展payloadlenght占16bit$mask_key=substr($buffer,4,4);$encoded_data=substr($buffer,8);}else{//ifpayloadlen=126byte//payloadlength本身占用7bit//扩展payloadlenght占用0bit$mask_key=substr($buffer,2,4);$encoded_data=substr($buffer,6);}//解码负载数据$decoded_data="";//对于每个用于解码操作的有效负载数据//解码规则在RFC文档中有详细描述for($index=0;$index
