摘要:服務(wù)端的開發(fā)離不開協(xié)議,的出現(xiàn)對于學(xué)習(xí)通信來說,無疑是非常好的教材。一旦握手成功,一個雙向連接通道就建立了。類型包括文本,二進(jìn)制,協(xié)議層信號等。目前一共有種類型,種保留類型。表示異或,表示取模。
websocket協(xié)議學(xué)習(xí) 概述服務(wù)端的開發(fā)離不開協(xié)議,swoole的出現(xiàn)對于學(xué)習(xí)通信來說,無疑是非常好的教材。非常推薦大家下載 Swoole Framework,其中包含了多種協(xié)議的php實(shí)現(xiàn),例如FTP,HTTP,Websocket等。本文大部分代碼都是受這個項(xiàng)目的啟發(fā),當(dāng)然學(xué)習(xí)的同時(shí)別忘了star一下這個項(xiàng)目。筆者本身計(jì)算機(jī)基礎(chǔ)較弱,寫這篇文章的同時(shí)也查了不少資料,如果有錯誤歡迎提出批評。
協(xié)議分為兩部分:握手,數(shù)據(jù)傳輸
客戶端發(fā)出的握手信息類似:
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Origin: http://example.com Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
服務(wù)器返回的握手信息類似:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Protocol: chat Sec-WebSocket-Version: 13
兩段信息的第一行大家應(yīng)該都比較熟悉,是HTTP協(xié)議中的Request-Line和Status-Line,RFC2616。下面接著出現(xiàn)的是無序的頭信息,這和HTTP協(xié)議相同。 一旦握手成功,一個雙向連接通道就建立了。
連接用于傳輸message, message由一個或多個frame組成。每個frame有一個類型,屬于同一個message的frame的類型都相同。類型包括:文本,二進(jìn)制,control frame(協(xié)議層信號)等。目前一共有6種類型,10種保留類型。
根據(jù)上面的客戶端頭信息可以看出,握手和HTTP是兼容的。WS的握手是HTTP的"升級版本"。
客戶端發(fā)送的握手請求必須
1. 是一個合法的HTTP請求
2. 方法是GET
3. 頭必須包含HOST字段
4. 頭必須包含Upgrade字段,值為websocket,可以看作是判斷請求為ws的標(biāo)志。
5. 頭必須包含Connection字段,值為Upgrade。
6. 頭必須包含Sec-WebSocket-Key字段,用于驗(yàn)證。
7. 如果請求來自瀏覽器,頭必須包含 Origin字段。
8. 頭必須包含Sec-WebSocket-Version字段,值為13
取Sec-WebSocket-Key字段的值,連接一個GUID字符串,"258EAFA5-E914-47DA-95CA-C5AB0DC85B11", sha1 hash一下,再base64_encode,得到的值作為字段Sec-WebSocket-Accept的值返回給客戶端。用php代碼表示:
"Sec-WebSocket-Accept" => base64_encode(sha1($key . static::GUID, true))
同時(shí),返回的狀態(tài)設(shè)置為101,其他狀態(tài)都表示握手沒有成功。 Connection,Upgrade字段作為HTTP升級版必須存在。一個握手返回如下:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Frame(幀)的結(jié)構(gòu)如下:
0 1 2 3 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 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
FIN: 1 bit, 標(biāo)記是否是最后一個message的最后一個片段
RSV1, RSV2, RSV3: 各 1 bit, 保留標(biāo)記,都為0
Opcode:4 bits, 是對payload data的說明,指明這個幀的類型。
0x0 表明為接著上一幀的連續(xù)幀
0x1 表明為text frame
0x2 表明為binary frame
0x3-7 保留
0x8 表示連接關(guān)閉
0x9 為ping
0xA 為pong
0xB-F 保留
- Mask: 1 bit, 指明Payload data是否被mask,如果為1,那么數(shù)據(jù)需要根據(jù)masking-key來unmask??蛻舳税l(fā)送的幀都是mask的。
- Payload length: 7 bits 或 7+16 bits 或 7+64 bits. 如果值為0-125,那么該值就是payload的長度;如果為126,那么接下來的2個byte表示payload長度(16bit, unsigned); 如果為127,那么接下來的8個bytes表示payload的長度(64bit, unsigned)。
- Masking-key: 0 或 4 bytes, 用于unmask payload data。
- Payload data: 長度為 Payload length, 可以分為 extension data + application data, 擴(kuò)展數(shù)據(jù)的長度計(jì)算方法是是事先商議好的,剩余的就是應(yīng)用數(shù)據(jù)。
masking-key是客戶端隨意指定的32bit長度值。從原始數(shù)據(jù)到masked數(shù)據(jù)的方式為:原始數(shù)據(jù)第i個字節(jié)的值 XOR masking-key的第(i%4)個字節(jié)的值。XOR表示異或,%表示取模。
片段化的作用當(dāng)傳遞一個未知長度的數(shù)據(jù)時(shí),可以不用一下子buffer全部的數(shù)據(jù)。尤其當(dāng)數(shù)據(jù)非常大時(shí),可以分多次buffer,包裝為frame來發(fā)送。
嘗試解析一個frame看到這里,我們已經(jīng)了解了frame的結(jié)構(gòu),是否想嘗試解析一個frame,官方文檔提供了幾段二進(jìn)制數(shù)據(jù),我們可以用來練習(xí)一下。我挑選了其中兩段, 代碼如下:
php> 7) & 0x1; $RSV1 = ($temp >> 6) & 0x1; $RSV2 = ($temp >> 5) & 0x1; $RSV3 = ($temp >> 4) & 0x1; $opcode = $temp & 0xf; echo "First byte: FIN is $FIN, RSV1-3 are $RSV1, $RSV2, $RSV3; Opcode is $opcode "; $temp = ord($data[$offset++]); $mask = ($temp >> 7) & 0x1; $payload_length = $temp & 0x7f; if($payload_length == 126){ $temp = substr($data, $offset, 2); $offset += 2; $temp = unpack("nl", $temp); $payload_length = $temp["l"]; }elseif($payload_length == 127){ $temp = substr($data, $offset, 8); $offset += 8; $temp = unpack("nl", $temp); $payload_length = $temp["l"]; } echo "mask is $mask, payload_length is $payload_length "; if($mask ==0){ $temp = substr($data, $offset); $content = ""; for ($i=0; $i < $payload_length; $i++) { $content .= $temp[$i]; } }else{ $masking_key = substr($data, $offset, 4); $offset += 4; $temp = substr($data, $offset); $content = ""; for ($i=0; $i < $payload_length; $i++) { $content .= chr(ord($temp[$i]) ^ ord($masking_key[$i%4])); } } echo "content is $content "; }
結(jié)果輸出如下圖:
到這里其實(shí)并不算完,ws協(xié)議還有很多很多規(guī)則,RFC文檔實(shí)在是太長了。比如,如何應(yīng)對每一種control frame,有詳細(xì)的說明;如何關(guān)閉連接;協(xié)議擴(kuò)展;錯誤處理;安全相關(guān);一些基本的內(nèi)容都能在swoole framework中找到對應(yīng)的代碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/21069.html
摘要:是一個基于擴(kuò)展實(shí)現(xiàn)的輕量級高性能的常駐內(nèi)存型的和應(yīng)用服務(wù)框架高度封裝了,,服務(wù)器,以及基于實(shí)現(xiàn)可擴(kuò)展的服務(wù),同時(shí)支持包方式安裝部署項(xiàng)目?;趯?shí)用,抽象事件處理類,實(shí)現(xiàn)與底層的回調(diào)的解耦,支持同步異步調(diào)用,內(nèi)置等常用組件等。 swoolefy swoolefy是一個基于swoole擴(kuò)展實(shí)現(xiàn)的輕量級高性能的常駐內(nèi)存型的API和Web應(yīng)用服務(wù)框架,高度封裝了http,websocket,ud...
摘要:那么,是否就無法用來開發(fā)雙向通信的應(yīng)用呢答案是否定的。內(nèi)置通信支持,可以與程序基于進(jìn)行雙向通信。通信協(xié)議于年被定為標(biāo)準(zhǔn),并由補(bǔ)充規(guī)范。前言 眾所周知,PHP用于開發(fā)基于HTTP協(xié)議的網(wǎng)站應(yīng)用非常便捷。而HTTP協(xié)議是一種單向的通信協(xié)議,只能接收客戶端的請求,然后響應(yīng)請求,不能主動向客戶端推送信息。因此,一些實(shí)時(shí)性要求比較高的應(yīng)用,如實(shí)時(shí)聊天、直播應(yīng)用、在線網(wǎng)頁游戲等,就不適合采用HTTP協(xié)議...
摘要:那么,是否就無法用來開發(fā)雙向通信的應(yīng)用呢答案是否定的。內(nèi)置通信支持,可以與程序基于進(jìn)行雙向通信。通信協(xié)議于年被定為標(biāo)準(zhǔn),并由補(bǔ)充規(guī)范。前言 眾所周知,PHP用于開發(fā)基于HTTP協(xié)議的網(wǎng)站應(yīng)用非常便捷。而HTTP協(xié)議是一種單向的通信協(xié)議,只能接收客戶端的請求,然后響應(yīng)請求,不能主動向客戶端推送信息。因此,一些實(shí)時(shí)性要求比較高的應(yīng)用,如實(shí)時(shí)聊天、直播應(yīng)用、在線網(wǎng)頁游戲等,就不適合采用HTTP協(xié)議...
摘要:源碼解讀系列一好難都跑不起來怎么破了解一下唄閱讀框架源碼第一步搞定環(huán)境小伙伴剛接觸的時(shí)候會感覺壓力有點(diǎn)大更直觀的說法是難開發(fā)組是不贊成難這個說法的的代碼都是實(shí)現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的開發(fā)組會用源碼解讀系列博客深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? doc...
摘要:源碼解讀系列一好難都跑不起來怎么破了解一下唄閱讀框架源碼第一步搞定環(huán)境小伙伴剛接觸的時(shí)候會感覺壓力有點(diǎn)大更直觀的說法是難開發(fā)組是不贊成難這個說法的的代碼都是實(shí)現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的開發(fā)組會用源碼解讀系列博客深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? doc...
閱讀 1053·2021-11-15 18:11
閱讀 3174·2021-09-22 15:33
閱讀 3469·2021-09-01 11:42
閱讀 2663·2021-08-24 10:03
閱讀 3630·2021-07-29 13:50
閱讀 2932·2019-08-30 14:08
閱讀 1282·2019-08-28 17:56
閱讀 2266·2019-08-26 13:57