佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > PHP實(shí)現(xiàn)WebSocket實(shí)例詳解

PHP實(shí)現(xiàn)WebSocket實(shí)例詳解

熱門(mén)標(biāo)簽:Linux服務(wù)器 電子圍欄 Mysql連接數(shù)設(shè)置 團(tuán)購(gòu)網(wǎng)站 科大訊飛語(yǔ)音識(shí)別系統(tǒng) 服務(wù)器配置 銀行業(yè)務(wù) 阿里云

WebSocket 是什么?

摘抄網(wǎng)上的一些解釋:

WebSocket 協(xié)議是基于 TCP 的一種新的網(wǎng)絡(luò)協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動(dòng)發(fā)送信息給客戶端。

WebSocket 通信協(xié)議于2011年被 IETF 定為標(biāo)準(zhǔn) RFC 6455,并被 RFC7936 所補(bǔ)充規(guī)范。

—— 百度百科

WebSocket 是一個(gè)持久化的協(xié)議,這是相對(duì)于 http 非持久化來(lái)說(shuō)的。

舉個(gè)簡(jiǎn)單的例子,http1.0 的生命周期是以 request 作為界定的,也就是一個(gè) request,一個(gè) response,對(duì)于 http 來(lái)說(shuō),本次 client 與 server 的會(huì)話到此結(jié)束;而在 http1.1 中,稍微有所改進(jìn),即添加了 keep-alive,也就是在一個(gè) http 連接中可以進(jìn)行多個(gè) request 請(qǐng)求和多個(gè) response 接受操作。然而在實(shí)時(shí)通信中,并沒(méi)有多大的作用,http 只能由 client 發(fā)起請(qǐng)求,server 才能返回信息,即 server 不能主動(dòng)向 client 推送信息,無(wú)法滿足實(shí)時(shí)通信的要求。而 WebSocket 可以進(jìn)行持久化連接,即 client 只需進(jìn)行一次握手,成功后即可持續(xù)進(jìn)行數(shù)據(jù)通信,值得關(guān)注的是 WebSocket 實(shí)現(xiàn) client 與 server 之間全雙工通信,即 server 端有數(shù)據(jù)更新時(shí)可以主動(dòng)推送給 client 端。

上圖是一個(gè)演示client和server之間建立WebSocket連接時(shí)握手部分

client 建立 WebSocket 時(shí)向服務(wù)器端請(qǐng)求的信息

GET /chat HTTP/1.1 
  Host: server.example.com 
  Upgrade: websocket //告訴服務(wù)器現(xiàn)在發(fā)送的是WebSocket協(xié)議
  Connection: Upgrade 
  Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== //是一個(gè)Base64 encode的值,這個(gè)是瀏覽器隨機(jī)生成的,用于驗(yàn)證服務(wù)器端返回?cái)?shù)據(jù)是否是WebSocket助理
  Sec-WebSocket-Protocol: chat, superchat 
  Sec-WebSocket-Version: 13 
  Origin: http://example.com

服務(wù)器獲取到 client 請(qǐng)求的信息后,根據(jù) WebSocket 協(xié)議對(duì)數(shù)據(jù)進(jìn)行處理并返回,其中要對(duì) Sec-WebSocket-Key 進(jìn)行加密等操作

HTTP/1.1 101 Switching Protocols 
  Upgrade: websocket //依然是固定的,告訴客戶端即將升級(jí)的是Websocket協(xié)議,而不是mozillasocket,lurnarsocket或者shitsocket
  Connection: Upgrade 
  Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= //這個(gè)則是經(jīng)過(guò)服務(wù)器確認(rèn),并且加密過(guò)后的 Sec-WebSocket-Key,也就是client要求建立WebSocket驗(yàn)證的憑證
  Sec-WebSocket-Protocol: chat

PHP 服務(wù)端

?php

if(($socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP))  0) {
    echo "socket_create() 失敗的原因是:".socket_strerror($sock)."\n";
}
if(($ret = socket_bind($socket,'127.0.0.1','9090'))  0) {
    echo "socket_bind() 失敗的原因是:".socket_strerror($ret)."\n";
}
if(($ret = socket_listen($socket,3))  0) {
    echo "socket_listen() 失敗的原因是:".socket_strerror($ret)."\n";
}

$all_sockets = [$socket];    // socket 集合

do {
    $copy_sockets = $all_sockets;   // 單獨(dú)拷貝一份

    // 因?yàn)榭蛻舳耸情L(zhǎng)連接,如果客戶端非正常斷開(kāi),服務(wù)端會(huì)在 socket_accept 阻塞,現(xiàn)在使用 select 非阻塞模式 socket
    if(socket_select($copy_sockets, $write, $except, 0) === false)
        exit('sosket_select error!');

    // 接收第一次 socket 連入,連入后移除服務(wù)端 socket
    if(in_array($socket, $copy_sockets)) {
        $client = socket_accept($socket);
        if($client) {
            $buf = socket_read($client, 1024);
            echo $buf;

            // 匹配 Sec-Websocket-Key 標(biāo)識(shí)
            if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/i",$buf,$match)) {
                // 需要將 Sec-WebSocket-Key 值累加字符串,并依次進(jìn)行 SHA-1 加密和 base64 加密
                $key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',true));
                // 拼湊響應(yīng)內(nèi)容
                $res= "HTTP/1.1 101 Switching Protocol".PHP_EOL
                    ."Upgrade: WebSocket".PHP_EOL
                    ."Connection: Upgrade".PHP_EOL
                    ."WebSocket-Location: ws://127.0.0.1:9090".PHP_EOL
                    ."Sec-WebSocket-Accept: " . $key .PHP_EOL.PHP_EOL;  // 注意這里,需要兩個(gè)換行
                // 向客戶端應(yīng)答 Sec-WebSocket-Accept
                socket_write($client, $res, strlen($res));
                // 向客戶端發(fā)送消息
                socket_write($client, buildMsg('socket ok'), 1024);

                // 加入客戶端 socket
                $all_sockets[] = $client;
            }
            // 移除服務(wù)端 socket
            $key = array_search($socket, $copy_sockets);
            unset($copy_sockets[$key]);

            // socket_close($client);
        }
    }

    // 循環(huán)所有客戶端 sockets
    foreach ($copy_sockets as $s) {
        // 獲取客戶端發(fā)給服務(wù)端的內(nèi)容
        $buf = socket_read($s, 8024);
        echo strlen($buf).'---'.PHP_EOL;
        // 代表客戶端主動(dòng)關(guān)閉
        if(strlen($buf)  9) {
            $key = array_search($s, $all_sockets);
            unset($all_sockets[$key]);
            socket_close($s);
            continue;
        }
        // 輸出
        echo getMsg($buf).PHP_EOL;
    }

}while(true);
socket_close($socket);

// 編碼服務(wù)端向客戶端發(fā)送的內(nèi)容
function buildMsg($msg) {
    $frame = [];
    $frame[0] = '81';
    $len = strlen($msg);
    if ($len  126) {
        $frame[1] = $len  16 ? '0' . dechex($len) : dechex($len);
    } else if ($len  65025) {
        $s = dechex($len);
        $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
    } else {
        $s = dechex($len);
        $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
    }
    $data = '';
    $l = strlen($msg);
    for ($i = 0; $i  $l; $i++) {
        $data .= dechex(ord($msg{$i}));
    }
    $frame[2] = $data;
    $data = implode('', $frame);
    return pack("H*", $data);
}

// 解析客戶端向服務(wù)端發(fā)送的內(nèi)容
function getMsg($buffer) {
    $res = '';
    $len = ord($buffer[1])  127;
    if ($len === 126) {
        $masks = substr($buffer, 4, 4);
        $data = substr($buffer, 8);
    } else if ($len === 127) {
        $masks = substr($buffer, 10, 4);
        $data = substr($buffer, 14);
    } else {
        $masks = substr($buffer, 2, 4);
        $data = substr($buffer, 6);
    }
    for ($index = 0; $index  strlen($data); $index++) {
        $res .= $data[$index] ^ $masks[$index % 4];
    }
    return $res;
}

客戶端

!DOCTYPE html>
html lang="en">
head>
    meta charset="UTF-8">
    title>Title/title>
    script>
        // 創(chuàng)建一個(gè)Socket實(shí)例
        var socket = new WebSocket('ws://localhost:9090');

        // 打開(kāi)Socket
        socket.onopen = function(event) {
            // 發(fā)送一個(gè)初始化消息
            socket.send("init msg");

        };
        socket.onmessage = function(event) {
            console.log('收到消息',event);

        };

        // 監(jiān)聽(tīng)Socket的關(guān)閉
        socket.onclose = function(event) {
            console.log('關(guān)閉監(jiān)聽(tīng)',event);
        };

        function  send()
        {
            socket.send("client msg");
        }
    /script>
/head>
body>
button onclick="send()">發(fā)送消息/button>
/body>
/html>

運(yùn)行測(cè)試:

Client

Server

到此這篇關(guān)于PHP實(shí)現(xiàn)WebSocket實(shí)例詳解的文章就介紹到這了,更多相關(guān)PHP實(shí)現(xiàn)WebSocket內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • php+websocket 實(shí)現(xiàn)的聊天室功能詳解
  • PHP 實(shí)現(xiàn) WebSocket 協(xié)議原理與應(yīng)用詳解
  • PHP框架實(shí)現(xiàn)WebSocket在線聊天通訊系統(tǒng)
  • PHP用swoole+websocket和redis實(shí)現(xiàn)web一對(duì)一聊天
  • ThinkPHP5.0框架結(jié)合Swoole開(kāi)發(fā)實(shí)現(xiàn)WebSocket在線聊天案例詳解
  • PHP實(shí)現(xiàn)websocket通信的方法示例
  • php redis 處理websocket聊天記錄的實(shí)例代碼

標(biāo)簽:江蘇 衡水 大理 棗莊 廣元 萍鄉(xiāng) 衢州 蚌埠

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《PHP實(shí)現(xiàn)WebSocket實(shí)例詳解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    襄垣县| 兴化市| 和平县| 岚皋县| 岳阳县| 大冶市| 板桥市| 拉萨市| 来安县| 喀喇| 民勤县| 济南市| 那坡县| 成都市| 浦东新区| 巩义市| 塔河县| 霍州市| 杭锦后旗| 游戏| 察隅县| 铅山县| 中卫市| 顺义区| 从化市| 文昌市| 太仓市| 津南区| 吴川市| 彝良县| 天水市| 潮安县| 台南市| 忻州市| 渭南市| 赣州市| 曲沃县| 新津县| 南宁市| 丹巴县| 灵宝市|