天天看點

利用Swoole實作PHP+websocket 聊天室

原文:http://www.jianshu.com/p/fedbb9d2d999# 作者:西嶺老濕

websocket

Websocket隻是一個網絡通信協定

就像 http、ftp等都是網絡通信的協定;不要多想;

相對于HTTP這種非持久的協定來說,Websocket是一個持久化網絡通信的協定;

WebSocket和HTTP的關系

WebSocket和HTTP的關系

利用Swoole實作PHP+websocket 聊天室

websocket+http.png

有交集,但是并不是全部。

Websocket隻是借用了HTTP的一部分協定來完成一次握手。(HTTP的三次握手,此處隻完成一次)

http和websocket 請求頭對比:

利用Swoole實作PHP+websocket 聊天室

http請求頭.jpg

利用Swoole實作PHP+websocket 聊天室

websocket請求頭.png

HTTP:

原來的時候,用戶端通過http(騎馬)帶着信請求伺服器,伺服器處理請求(寫回信),再次通過http(騎馬)傳回;連結斷開;

WebSocket:

用戶端通過http(騎馬)帶着信請求伺服器,但同時,攜帶了Upgrade:websocket和Connection:Upgrade(兩根管子),伺服器如果支援WebSocket協定(有兩根管子的接口),使用Websocket協定傳回可用資訊(丢棄馬匹),此後資訊的傳遞,均使用這兩個管子,除非有一方人為的将管子切斷;若伺服器不支援,用戶端請求連結失敗,傳回錯誤資訊;

http和websocket 響應頭對比:

利用Swoole實作PHP+websocket 聊天室

http響應頭.jpg

利用Swoole實作PHP+websocket 聊天室

websocket響應頭.jpg

websocket和ajax輪詢、long poll的差別

首先是 ajax輪詢 ,ajax輪詢的原理非常簡單,讓浏覽器隔個幾秒就發送一次請求,詢問伺服器是否有新資訊

場景再現:

用戶端:啦啦啦,有沒有新資訊(Request)

服務端:沒有(Response)

用戶端:啦啦啦,有沒有新資訊(Request)

服務端:沒有。。(Response)

用戶端:啦啦啦,有沒有新資訊(Request)

服務端:你好煩啊,沒有啊。。(Response)

用戶端:啦啦啦,有沒有新消息(Request)

服務端:好啦好啦,有啦給你。(Response)

用戶端:啦啦啦,有沒有新消息(Request)

服務端:。。。沒。。。。沒。。沒有

long poll 其實原理跟 ajax輪詢 差不多,都是采用輪詢的方式,不在論述;

從上面可以看出,輪詢其實就是在不斷地建立HTTP連接配接,然後等待服務端處理,可以展現HTTP協定的另外一個特點,被動性。同時,http的每一次請求與響應結束後,伺服器将用戶端資訊全部丢棄,下次請求,必須攜帶身份資訊(cookie),無狀态性;

Websocket的出現,幹淨利落的解決了這些問題;

是以上面的情景可以做如下修改。

用戶端:啦啦啦,我要建立Websocket協定,需要的服務:chat,Websocket協定版本:17(HTTP Request)

服務端:ok,确認,已更新為Websocket協定(HTTP Protocols Switched)

用戶端:麻煩你有資訊的時候推送給我噢。。

服務端:ok,有的時候會告訴你的。

用戶端:balab開始鬥圖alabala

服務端:蒼井空ala

用戶端:流鼻血了,我擦……

服務端:哈哈布爾教育牛逼啊哈哈哈哈

服務端:笑死我了哈哈

Swoole

但是,為了用PHP配合HTML5完成一次WebSocket請求和響應,哥走過千山萬水,在密林深處,發現了Swoole : http://www.swoole.com/;

PHP語言的異步、并行、高性能網絡通信架構,使用純C語言編寫,提供了PHP語言的異步多線程伺服器,異步TCP/UDP網絡用戶端,異步MySQL,資料庫連接配接池,AsyncTask,消息隊列,毫秒定時器,異步檔案讀寫,異步DNS查詢。

支援的服務:

HttpServer

WebSocket Server

TCP Server

TCP Client

Async-IO(異步)

Task(定時任務)

環境依賴:

僅支援Linux,FreeBSD,MacOS,3類作業系統

Linux核心版本2.3.32以上

PHP5.3.10以上版本

gcc4.4以上版本或者clang

cmake2.4+,編譯為libswoole.so作為C/C++庫時需要使用cmake

安裝:

必須保證系統中有以下這些軟體:

php-5.3.10 或更高版本

gcc-4.4 或更高版本

make

autoconf

Swoole是作為PHP擴充來運作的

安裝(root權限):

cd swoole

phpize

./configure

make

sudo make install

配置php.ini

extension=swoole.so

想研究Swoole的同學,自己去看手冊(雖然寫的不好,但是還是能看懂的)

做一個聊天室

伺服器端:socket.php

//建立websocket伺服器對象,監聽0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9502);

//監聽WebSocket連接配接打開事件
$ws->on('open', function ($ws, $request) {
    $fd[] = $request->fd;
    $GLOBALS['fd'][] = $fd;
    //$ws->push($request->fd, "hello, welcome\n");
});

//監聽WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
    $msg =  'from'.$frame->fd.":{$frame->data}\n";
//var_dump($GLOBALS['fd']);
//exit;
    foreach($GLOBALS['fd'] as $aa){
        foreach($aa as $i){
            $ws->push($i,$msg);
        }
    }
   // $ws->push($frame->fd, "server: {$frame->data}");
    // $ws->push($frame->fd, "server: {$frame->data}");
});

//監聽WebSocket連接配接關閉事件
$ws->on('close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});

$ws->start();
           

用戶端:Socket.html

<!DOCTYPE html>
<html >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="msg"></div>
<input type="text" id="text">
<input type="submit" value="發送資料" οnclick="song()">
</body>
<script>
    var msg = document.getElementById("msg");
    var wsServer = 'ws://192.168.1.253:9502';
    //調用websocket對象建立連接配接:
    //參數:ws/wss(加密)://ip:port (字元串)
    var websocket = new WebSocket(wsServer);
    //onopen監聽連接配接打開
    websocket.onopen = function (evt) {
        //websocket.readyState 屬性:
        /*
        CONNECTING    0    The connection is not yet open.
        OPEN    1    The connection is open and ready to communicate.
        CLOSING    2    The connection is in the process of closing.
        CLOSED    3    The connection is closed or couldn't be opened.
        */
        msg.innerHTML = websocket.readyState;
    };

    function song(){
        var text = document.getElementById('text').value;
        document.getElementById('text').value = '';
        //向伺服器發送資料
        websocket.send(text);
    }
      //監聽連接配接關閉
//    websocket.onclose = function (evt) {
//        console.log("Disconnected");
//    };

    //onmessage 監聽伺服器資料推送
    websocket.onmessage = function (evt) {
        msg.innerHTML += evt.data +'<br>';
//        console.log('Retrieved data from server: ' + evt.data);
    };
//監聽連接配接錯誤資訊
//    websocket.onerror = function (evt, e) {
//        console.log('Error occured: ' + evt.data);
//    };

</script>
</html>
           

websocket API 手冊:

https://developer.mozilla.org/en-US/docs/Web/API/WebSocket