在現(xiàn)代的互聯(lián)網(wǎng)應(yīng)用中,實(shí)時(shí)性和交互性是越來(lái)越受到關(guān)注的。而在實(shí)現(xiàn)實(shí)時(shí)雙向通信時(shí),WebSocket成為了一種非常好的解決方案。在PHP語(yǔ)言中,實(shí)現(xiàn)WebSocket可以使用Swoole擴(kuò)展庫(kù),其提供了基于TCP協(xié)議的WebSocket服務(wù)器實(shí)現(xiàn)。
具體來(lái)說(shuō),Swoole提供了一個(gè)Swoole\Websocket\Server類,通過(guò)創(chuàng)建該類的實(shí)例,我們可以輕松地啟動(dòng)一個(gè)WebSocket服務(wù)器并提供以下幾個(gè)常用的事件回調(diào):
$server = new Swoole\Websocket\Server("127.0.0.1", 9502); //連接成功后回調(diào) $server->on('open', function ($server, $request) { echo "client {$request->fd} connected\n"; }); //收到客戶端消息后回調(diào) $server->on('message', function ($server, $frame) { echo "receive from {$frame->fd}:{$frame->data}, opcode:{$frame->opcode},fin:{$frame->finish}\n"; $server->push($frame->fd, "this is server"); }); //連接關(guān)閉后回調(diào) $server->on('close', function ($server, $fd) { echo "client {$fd} closed\n"; }); $server->start();
以上代碼的邏輯非常簡(jiǎn)單,我們創(chuàng)建一個(gè)WebSocket服務(wù)器,然后提供客戶端與服務(wù)器建立連接、接收消息和關(guān)閉連接時(shí)的回調(diào)函數(shù)。為了發(fā)送消息給客戶端,我們可以通過(guò)服務(wù)器實(shí)例的push方法來(lái)實(shí)現(xiàn)。
值得一提的是,在WebSocket中,消息分為不同的類型,對(duì)應(yīng)frame中的opcode屬性。常見(jiàn)的類型有:
- 0x1 TEXT類型
- 0x2 BINARY類型
- 0x8 CLOSE類型
- 0x9 PING類型
- 0xA PONG類型
在實(shí)際使用過(guò)程中,我們往往需要處理的是TEXT或BINARY類型的消息:我們可以通過(guò)客戶端發(fā)送的消息類型來(lái)判斷frame的opcode屬性,進(jìn)而做出不同的響應(yīng)或邏輯。
下面我們通過(guò)簡(jiǎn)單的示例程序來(lái)演示如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的聊天室:
class ChatRoom { private $server; private $users = []; public function __construct($host, $port) { $this->server = new Swoole\Websocket\Server($host, $port); $this->server->on('open', [$this, 'onOpen']); $this->server->on('message', [$this, 'onMessage']); $this->server->on('close', [$this, 'onClose']); $this->server->start(); } public function onOpen($server, $request) { echo "client {$request->fd} joined\n"; $this->users[$request->fd] = [ 'fd' =>$request->fd, 'name' =>"Guest-" . rand(1000, 9999), ]; $message = [ 'name' =>'系統(tǒng)消息', 'content' =>"{$this->users[$request->fd]['name']}加入聊天室", ]; $this->broadcast(json_encode($message)); } public function onMessage($server, $frame) { $content = json_decode($frame->data, true); $user = $this->users[$frame->fd]; if ($content['event'] == 'rename') { $name = $content['name'] ?? ''; if (empty($name)) { return; } $user['name'] = $name; $this->users[$frame->fd] = $user; $message = [ 'name' =>'系統(tǒng)消息', 'content' =>"{$user['name']}更新了昵稱", ]; $this->broadcast(json_encode($message)); return; } $message = [ 'name' =>$user['name'], 'content' =>$content['message'] ?? '', ]; $this->broadcast(json_encode($message)); } public function onClose($server, $fd) { echo "client {$fd} closed\n"; $user = $this->users[$fd]; unset($this->users[$fd]); $message = [ 'name' =>'系統(tǒng)消息', 'content' =>"{$user['name']}退出聊天室", ]; $this->broadcast(json_encode($message)); } public function broadcast($message) { foreach ($this->users as $user) { $this->server->push($user['fd'], $message); } } } //啟動(dòng)聊天室服務(wù)器 new ChatRoom('127.0.0.1', 9502);
在以上代碼中,我們創(chuàng)建了一個(gè)名為ChatRoom的類,用于實(shí)現(xiàn)聊天室的功能。該類中有以下幾個(gè)方法:
- __construct:創(chuàng)建WebSocket服務(wù)器并注冊(cè)事件回調(diào)函數(shù)。
- onOpen:連接成功時(shí)回調(diào),用于記錄用戶信息并向其他用戶廣播該用戶的加入信息。
- onMessage:收到消息時(shí)回調(diào),用于判斷消息類型,處理重命名事件和向其他用戶廣播用戶發(fā)出的消息。
- onClose:關(guān)閉連接時(shí)回調(diào),用于記錄用戶退出信息并向其他用戶廣播用戶的退出信息。
- broadcast:向所有連接的用戶廣播消息。
通過(guò)以上代碼,我們可以使用WebSocket實(shí)現(xiàn)一個(gè)基礎(chǔ)的聊天室,體驗(yàn)實(shí)時(shí)雙向通信的魅力。
總的來(lái)說(shuō),WebSocket是一種非常好的實(shí)現(xiàn)實(shí)時(shí)雙向通信的解決方案,Swoole擴(kuò)展庫(kù)提供了非常方便易用的實(shí)現(xiàn)方式,可以大大簡(jiǎn)化實(shí)現(xiàn)的難度。通過(guò)簡(jiǎn)單的示例程序,我們可以體驗(yàn)實(shí)時(shí)雙向通信的魅力。