在這里是關(guān)于Websocket在 php 中的實(shí)現(xiàn),這篇文章里,我主要對(duì)websocket 協(xié)議進(jìn)行一個(gè)簡單的介紹。
Websocket 業(yè)務(wù)模型
瀏覽器端的websocket 請(qǐng)求一般是
// javacsript
var ws = new WebSocket("ws://127.0.0.1:4000");
ws.onopen = function(){
console.log("succeed");
};
ws.onerror = function(){
console.log(“error”);
};
ws.onmessage = function(e){
console.log(e);
}
當(dāng) new 一個(gè) websocket 對(duì)象之后,就會(huì)向服務(wù)器發(fā)送一個(gè) get 請(qǐng)求
這個(gè)請(qǐng)求是對(duì)摸個(gè)服務(wù)器的端口發(fā)送的,一般的話,會(huì)預(yù)先在服務(wù)器將一個(gè)socket 綁定到一個(gè)端口上,客戶端和服務(wù)器端在這個(gè)預(yù)定的端口上通信(我這里綁定的就是 4000 端口,默認(rèn)情況下,websocke 使用 80 端口)。
然后,在服務(wù)器端的socket監(jiān)聽到這個(gè)packet 之后就生成一個(gè)新的 socket,將發(fā)送過來的數(shù)據(jù)中的 Sec-WebSocket-Key 解析出來,然后按照把“Sec-WebSocket-Ke”加上一個(gè)魔幻字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用SHA-1加密,之后進(jìn)行BASE-64編碼,將結(jié)果做為“Sec-WebSocket-Accept”頭的值,返回給客戶端。
客戶端收到這個(gè)之后,就會(huì)將 通信協(xié)議 upgrade 到 websocket 協(xié)議。
然后就會(huì)在這個(gè)持久的通道下進(jìn)行通信,包括瀏覽器的詢問,服務(wù)器的push,雙方是在一個(gè)全雙工的狀態(tài)下相互通信。 切換后的websocket 協(xié)議中的 數(shù)據(jù)傳輸幀的格式(此時(shí)不再使用html協(xié)議) 官方文檔給出的說明:
直接看這個(gè),誰都會(huì)有點(diǎn)頭大: 我花了一幅圖來簡單的解釋這個(gè) frame 的結(jié)構(gòu)。
各字段的解釋:
FIN 1bit 表示信息的最后一幀,flag,也就是標(biāo)記符
RSV 1-3 1bit each 以后備用的 默認(rèn)都為 0
Opcode 4bit 幀類型,
Mask 1bit 掩碼,是否加密數(shù)據(jù),默認(rèn)必須置為1
Payload len 7bit 數(shù)據(jù)的長度,當(dāng)這個(gè)7 bit的數(shù)據(jù) == 126 時(shí),后面的2 個(gè)字節(jié)也是表示數(shù) 據(jù)長度,當(dāng)它 == 127 時(shí),后面的 8 個(gè)字節(jié)表示數(shù)據(jù)長度Masking-key 1 or 4 bit 掩碼Payload data playload len bytes 數(shù)據(jù)
所以我們這里的代碼,通過判斷 Playload len的值,來用 substr 截取 Masking-key 和 PlayloadData。
根據(jù)掩碼解析數(shù)據(jù)的方法是:
for( i = 0; i < data.length ; i++){
orginalData += data[i] ^ maskingKey[i mod 4];
}
在PHP中,當(dāng)我們收到數(shù)據(jù)之后,按照這里的格式截取數(shù)據(jù),并將其按照這里的方法解析后就得到了瀏覽器發(fā)送過來的數(shù)據(jù)。 當(dāng)我們想把數(shù)據(jù)發(fā)送給瀏覽器時(shí),也要按照這個(gè)格式組裝frame。 這里是我的方法:
function frame($s){
$a = str_split($s, 125);
if (count($a) == 1){
return "\x81" . chr(strlen($a[0])) . $a[0];
}
$ns = "";
foreach ($a as $o){
$ns .= "\x81" . chr(strlen($o)) . $o;
}
return $ns;
}
強(qiáng)行將要發(fā)送的數(shù)據(jù)分割成 125 Byte / frame,這樣 playload len 只需要 7 bits。也就是直接將數(shù)據(jù)的長度的ascall碼拼接上去,然后后面跟上要發(fā)送的數(shù)據(jù)。 每一個(gè) frame 前面加的 ‘\x81’ 用二進(jìn)制就是: 1000 0001 1000 :
1 是 FIN
000 是三個(gè)備用的bit
0001 : 指的是 opcode 官方的解釋:
可以設(shè)置 opcode的值,來告訴瀏覽器這個(gè)frame的數(shù)據(jù)屬性。