五分鐘教你寫(xiě)超簡(jiǎn)單的swoole聊天室
原本我是準(zhǔn)備接著寫(xiě)我那個(gè)多進(jìn)程教程的,今天心血來(lái)潮想看看swoole的websocket,
swoole-1.7.9 增加了內(nèi)置的websocket服務(wù)器支持,通過(guò)幾行PHP代碼就可以寫(xiě)出一個(gè)異步非阻塞多進(jìn)程的WebSocket服務(wù)器。
swoole_websocket_server 繼承自 swoole_http_server,如果設(shè)置了onRequest回調(diào),websocket服務(wù)器也可以同時(shí)作為http服務(wù)器。
- $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->start();
我看了看官網(wǎng)的demo,覺(jué)得看起來(lái)很簡(jiǎn)單嘛,
- <?php
- //官網(wǎng)demo
- $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";//$request->fd 是客戶端id
- });
- $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");//$frame->fd 是客戶端id,$frame->data是客戶端發(fā)送的數(shù)據(jù)
- //服務(wù)端向客戶端發(fā)送數(shù)據(jù)是用 $server->push( '客戶端id' , '內(nèi)容')
- });
- $server->on('close', function ($ser, $fd) {
- echo "client {$fd} closed\n";
- });
- $server->start();
我就是喜歡這種簡(jiǎn)單易懂的demo ,每行代碼意思一看就明白
服務(wù)端有了,我找點(diǎn)客戶端的js代碼
火狐的MDN
- <!DOCTYPE html>
- <html>
- <head>
- <title></title>
- <meta charset="UTF-8">
- <script type="text/javascript">
- var exampleSocket = new WebSocket("ws://0.0.0.0:9501");
- exampleSocket.onopen = function (event) {
- exampleSocket.send("親愛(ài)的服務(wù)器!我連上你啦!");
- };
- exampleSocket.onmessage = function (event) {
- console.log(event.data);
- }
- </script>
- </head>
- <body>
- <input type="text" id="content">
- <button onclick="exampleSocket.send( document.getElementById('content').value )">發(fā)送</button>
- </body>
- </html>
***命令行運(yùn)行php文件,之后瀏覽器打開(kāi)html文件,
F12打開(kāi)調(diào)試界面看console,ok , 沒(méi)有問(wèn)題
這個(gè)時(shí)候我突然想到一個(gè)事情,因?yàn)槲易龆噙M(jìn)程的那個(gè)教程里,在主進(jìn)程中會(huì)將所有的子進(jìn)程的句柄存起來(lái),以后進(jìn)行進(jìn)程間通訊用。
那么 我將所有的客戶端的鏈接存起來(lái)存成數(shù)組,每當(dāng)一個(gè)客戶端發(fā)送消息時(shí),我就遍歷這個(gè)客戶端數(shù)組,將消息群發(fā)一遍,不久實(shí)現(xiàn)了聊天室了嗎?
然后就,服務(wù)端代碼成了這個(gè)樣子
- <?php
- $map = array();//客戶端集合
- $server = new swoole_websocket_server("0.0.0.0", 9501);
- $server->on('open', function (swoole_websocket_server $server, $request) {
- global $map;//客戶端集合
- $map[$request->fd] = $request->fd;//***連上時(shí)存起來(lái)
- });
- $server->on('message', function (swoole_websocket_server $server, $frame) {
- global $map;//客戶端集合
- $data = $frame->data;
- foreach($map as $fd){
- $server->push($fd , $data);//循環(huán)廣播
- }
- });
- $server->on('close', function ($ser, $fd) {
- echo "client {$fd} closed\n";
- });
- $server->start();
哈哈 , 我覺(jué)得這樣就大功告成了,結(jié)果發(fā)現(xiàn)自己是 圖樣圖森破
大家可以自己試試,運(yùn)行php后 , 瀏覽器打開(kāi)兩個(gè)頁(yè)面,看看console.log的內(nèi)容是什么
運(yùn)行良好,可是并沒(méi)有實(shí)現(xiàn)我們說(shuō)的那種聊天效果。
找找原因吧。
我***反映看看$map里面是什么,就輸出看看,結(jié)果發(fā)現(xiàn)這個(gè)map里面只有一個(gè)元素。
唉,不對(duì)啊,我這是全局變量,難道不應(yīng)該是有幾個(gè)客戶端鏈接,就有幾個(gè)元素嗎?
這是怎么回事啊,竟然沒(méi)有保存到所有客戶端id?
到了這一步,我解決不了map變量的這個(gè)問(wèn)題了,然后我就想看看那個(gè)fd是什么東西,
老規(guī)矩 var_dump輸出 , 發(fā)現(xiàn)fd就是 int類型的數(shù)字,并且是自增的
這好辦了,不就是數(shù)字嘛
于是呼,我就這樣做
變量存不了,我搞不定,我存文本里嘛。
最終版 websocket.php
- <?php
- $server = new swoole_websocket_server("0.0.0.0", 9501);
- $server->on('open', function (swoole_websocket_server $server, $request) {
- file_put_contents( __DIR__ .'/log.txt' , $request->fd);
- });
- $server->on('message', function (swoole_websocket_server $server, $frame) {
- global $client;
- $data = $frame->data;
- $m = file_get_contents( __DIR__ .'/log.txt');
- for ($i=1 ; $i<= $m ; $i++) {
- echo PHP_EOL . ' i is ' . $i . ' data is '.$data . ' m = ' . $m;
- $server->push($i, $data );
- }
- });
- $server->on('close', function ($ser, $fd) {
- echo "client {$fd} closed\n";
- });
- $server->start();
再次打開(kāi)html文件,多個(gè)頁(yè)面進(jìn)行輸入觀察,ok,可以了。
當(dāng)然,作為聊天室,我這寫(xiě)的也過(guò)于簡(jiǎn)陋了,界面大家自己可以寫(xiě)的好看一些(因?yàn)槲覒械膶?xiě)界面)
還有,每次的發(fā)送聊天的記錄,應(yīng)該存起來(lái),這樣,如果有新的連接連過(guò)來(lái)的時(shí)候,先把以前的聊天記錄發(fā)過(guò)去,這樣,我想體驗(yàn)更好一些
然后,大家可以愉快的聊天了。哈哈