WIKI:php
問:websocket協議雖然和http協議不一樣,可是兼容於http協議,如何判斷客戶端鏈接使用的是http協議?web
答:經過使用 $server->connection_info($fd) 獲取鏈接信息,返回的數組中有一項爲 websocket_status,根據此狀態能夠判斷是否爲 WebSocket 客戶端。算法
---------- 正文的分割線 -------------編程
Swoole\WebSocket\Server繼承自Swoole\Http\Server,因此websocket server支持http server和tcp server的全部事件。另外,新增長了如下3個事件:數組
· onMessage (必選)瀏覽器
· onOpen 和 onHandShake (可選)服務器
事件詳解websocket
onHandShake:WebSocket 創建鏈接後進行握手。WebSocket 服務器會自動進行 handshake 握手的過程,若是用戶但願本身進行握手處理,能夠設置 onHandShake 事件回調函數。socket
onHandShake(Swoole\Http\Request $request, Swoole\Http\Response $response);
· onHandShake 事件回調是可選的,須要自行處理 handshake 的時候,再設置這個回調函數。若是您不須要 「自定義」 握手過程,那麼不要設置該回調,用 Swoole 默認的握手便可。
· 設置 onHandShake 回調函數後不會再觸發 onOpen 事件,須要應用代碼自行處理
· onHandShake 中必須調用 response->status() 設置狀態碼爲 101 並調用 response->end() 響應,不然會握手失敗.
· 內置的握手協議爲 Sec-WebSocket-Version: 13,低版本瀏覽器須要自行實現握手
· 可使用 server->defer 調用 onOpen 邏輯tcp
示例:
$server->on('handshake', function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) { // print_r( $request->header ); // if (若是不知足我某些自定義的需求條件,那麼返回end輸出,返回false,握手失敗) { // $response->end(); // return false; // } // websocket握手鍊接算法驗證 $secWebSocketKey = $request->header['sec-websocket-key']; $patten = '#^[+/0-9A-Za-z]{21}[AQgw]==$#'; if (0 === preg_match($patten, $secWebSocketKey) || 16 !== strlen(base64_decode($secWebSocketKey))) { $response->end(); return false; } echo $request->header['sec-websocket-key']; $key = base64_encode( sha1( $request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true ) ); $headers = [ 'Upgrade' => 'websocket', 'Connection' => 'Upgrade', 'Sec-WebSocket-Accept' => $key, 'Sec-WebSocket-Version' => '13', ]; // WebSocket connection to 'ws://127.0.0.1:9502/' // failed: Error during WebSocket handshake: // Response must not include 'Sec-WebSocket-Protocol' header if not present in request: websocket if (isset($request->header['sec-websocket-protocol'])) { $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol']; } foreach ($headers as $key => $val) { $response->header($key, $val); } $response->status(101); $response->end(); });
onOpen:當 WebSocket 客戶端與服務器創建鏈接並完成握手後會回調此函數。
onOpen(Swoole\WebSocket\Server $server, Swoole\Http\Request $request);
· $request 是一個 HTTP 請求對象,包含了客戶端發來的握手請求信息
· onOpen 事件函數中能夠調用 push 向客戶端發送數據或者調用 close 關閉鏈接
· onOpen 事件回調是可選的
onMessage:當服務器收到來自客戶端的數據幀時會回調此函數。
onMessage(Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame)
· $frame 是 Swoole\WebSocket\Frame 對象,包含了客戶端發來的數據幀信息
· onMessage 回調必須被設置,未設置服務器將沒法啓動
· 客戶端發送的 ping 幀不會觸發 onMessage,底層會自動回覆 pong 包,也可設置 open_websocket_ping_frame 參數手動處理
關於Swoole\WebSocket\Frame $frame
· $frame->data 若是是文本類型,編碼格式必然是 UTF-8,這是 WebSocket 協議規定的
示例:
面向過程寫法:
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501); $server->on('open', function (Swoole\WebSocket\Server $server, $request) { echo "server: handshake success with fd{$request->fd}\n"; }); $server->on('message', function (Swoole\WebSocket\Server $server, $frame) { echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; $server->push($frame->fd, "this is server"); }); $server->on('close', function ($ser, $fd) { echo "client {$fd} closed\n"; }); $server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { global $server;//調用外部的server // $server->connections 遍歷全部websocket鏈接用戶的fd,給全部用戶推送 foreach ($server->connections as $fd) { // 須要先判斷是不是正確的websocket鏈接,不然有可能會push失敗 if ($server->isEstablished($fd)) { $server->push($fd, $request->get['message']); } } }); $server->start();
面向對象寫法:
class WebSocketTest { public $server; public function __construct() { $this->server = new Swoole\WebSocket\Server("0.0.0.0", 9501); $this->server->on('open', function (Swoole\WebSocket\Server $server, $request) { echo "server: handshake success with fd{$request->fd}\n"; }); $this->server->on('message', function (Swoole\WebSocket\Server $server, $frame) { echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; $server->push($frame->fd, "this is server"); }); $this->server->on('close', function ($ser, $fd) { echo "client {$fd} closed\n"; }); $this->server->on('request', function ($request, $response) { // 接收http請求從get獲取message參數的值,給用戶推送 // $this->server->connections 遍歷全部websocket鏈接用戶的fd,給全部用戶推送 foreach ($this->server->connections as $fd) { // 須要先判斷是不是正確的websocket鏈接,不然有可能會push失敗 if ($this->server->isEstablished($fd)) { $this->server->push($fd, $request->get['message']); } } }); $this->server->start(); } } new WebSocketTest();
--------------------------- 我是可愛的分割線 ----------------------------
最後博主借地宣傳一下,漳州編程小組招新了,這是一個面向漳州青少年信息學/軟件設計的學習小組,有意向的同窗點擊連接,聯繫我吧。