成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

PHP websocket之聊天室實(shí)現(xiàn)

weknow619 / 1736人閱讀

摘要:在服務(wù)器編程中函數(shù)把進(jìn)程變?yōu)橐粋€(gè)服務(wù)器,并指定相應(yīng)的套接字變?yōu)楸粍?dòng)連接其中的能存儲(chǔ)的請(qǐng)求不明的數(shù)目。

PHP部分

 [
     *                        info
     *                      ]
     *      ]
     *  todo 解釋socket與file號(hào)對(duì)應(yīng)
     */
    private $sockets = [];
    private $master;
    public function __construct($host, $port) {
        try {
            $this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            // 設(shè)置IP和端口重用,在重啟服務(wù)器后能重新使用此端口;
            socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);
            // 將IP和端口綁定在服務(wù)器socket上;
            socket_bind($this->master, $host, $port);
            // listen函數(shù)使用主動(dòng)連接套接口變?yōu)楸贿B接套接口,使得一個(gè)進(jìn)程可以接受其它進(jìn)程的請(qǐng)求,從而成為一個(gè)服務(wù)器進(jìn)程。在TCP服務(wù)器編程中l(wèi)isten函數(shù)把進(jìn)程變?yōu)橐粋€(gè)服務(wù)器,并指定相應(yīng)的套接字變?yōu)楸粍?dòng)連接,其中的能存儲(chǔ)的請(qǐng)求不明的socket數(shù)目。
            socket_listen($this->master, self::LISTEN_SOCKET_NUM);
        } catch (Exception $e) {
            $err_code = socket_last_error();
            $err_msg = socket_strerror($err_code);
            $this->error([
                "error_init_server",
                $err_code,
                $err_msg
            ]);
        }
        $this->sockets[0] = ["resource" => $this->master];
        $pid = posix_getpid();
        $this->debug(["server: {$this->master} started,pid: {$pid}"]);
        while (true) {
            try {
                $this->doServer();
            } catch (Exception $e) {
                $this->error([
                    "error_do_server",
                    $e->getCode(),
                    $e->getMessage()
                ]);
            }
        }
    }
    private function doServer() {
        $write = $except = NULL;
        $sockets = array_column($this->sockets, "resource");
        $read_num = socket_select($sockets, $write, $except, NULL);
        // select作為監(jiān)視函數(shù),參數(shù)分別是(監(jiān)視可讀,可寫(xiě),異常,超時(shí)時(shí)間),返回可操作數(shù)目,出錯(cuò)時(shí)返回false;
        if (false === $read_num) {
            $this->error([
                "error_select",
                $err_code = socket_last_error(),
                socket_strerror($err_code)
            ]);
            return;
        }
        foreach ($sockets as $socket) {
            // 如果可讀的是服務(wù)器socket,則處理連接邏輯
            if ($socket == $this->master) {
                $client = socket_accept($this->master);
                // 創(chuàng)建,綁定,監(jiān)聽(tīng)后accept函數(shù)將會(huì)接受socket要來(lái)的連接,一旦有一個(gè)連接成功,將會(huì)返回一個(gè)新的socket資源用以交互,如果是一個(gè)多個(gè)連接的隊(duì)列,只會(huì)處理第一個(gè),如果沒(méi)有連接的話,進(jìn)程將會(huì)被阻塞,直到連接上.如果用set_socket_blocking或socket_set_noblock()設(shè)置了阻塞,會(huì)返回false;返回資源后,將會(huì)持續(xù)等待連接。
                if (false === $client) {
                    $this->error([
                        "err_accept",
                        $err_code = socket_last_error(),
                        socket_strerror($err_code)
                    ]);
                    continue;
                } else {
                    self::connect($client);
                    continue;
                }
            } else {
                // 如果可讀的是其他已連接socket,則讀取其數(shù)據(jù),并處理應(yīng)答邏輯
                $bytes = @socket_recv($socket, $buffer, 2048, 0);
                if ($bytes < 9) {
                    $recv_msg = $this->disconnect($socket);
                } else {
                    if (!$this->sockets[(int)$socket]["handshake"]) {
                        self::handShake($socket, $buffer);
                        continue;
                    } else {
                        $recv_msg = self::parse($buffer);
                    }
                }
                array_unshift($recv_msg, "receive_msg");
                $msg = self::dealMsg($socket, $recv_msg);
                $this->broadcast($msg);
            }
        }
    }
    /**
     * 將socket添加到已連接列表,但握手狀態(tài)留空;
     *
     * @param $socket
     */
    public function connect($socket) {
        socket_getpeername($socket, $ip, $port);
        $socket_info = [
            "resource" => $socket,
            "uname" => "",
            "handshake" => false,
            "ip" => $ip,
            "port" => $port,
        ];
        $this->sockets[(int)$socket] = $socket_info;
        $this->debug(array_merge(["socket_connect"], $socket_info));
    }
    /**
     * 客戶端關(guān)閉連接
     *
     * @param $socket
     *
     * @return array
     */
    private function disconnect($socket) {
        $recv_msg = [
            "type" => "logout",
            "content" => $this->sockets[(int)$socket]["uname"],
        ];
        unset($this->sockets[(int)$socket]);
        return $recv_msg;
    }
    /**
     * 用公共握手算法握手
     *
     * @param $socket
     * @param $buffer
     *
     * @return bool
     */
    public function handShake($socket, $buffer) {
        // 獲取到客戶端的升級(jí)密匙
        $line_with_key = substr($buffer, strpos($buffer, "Sec-WebSocket-Key:") + 18);
        $key = trim(substr($line_with_key, 0, strpos($line_with_key, "
")));
        // 生成升級(jí)密匙,并拼接websocket升級(jí)頭
        $upgrade_key = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));// 升級(jí)key的算法
        $upgrade_message = "HTTP/1.1 101 Switching Protocols
";
        $upgrade_message .= "Upgrade: websocket
";
        $upgrade_message .= "Sec-WebSocket-Version: 13
";
        $upgrade_message .= "Connection: Upgrade
";
        $upgrade_message .= "Sec-WebSocket-Accept:" . $upgrade_key . "

";
        socket_write($socket, $upgrade_message, strlen($upgrade_message));// 向socket里寫(xiě)入升級(jí)信息
        $this->sockets[(int)$socket]["handshake"] = true;
        socket_getpeername($socket, $ip, $port);
        $this->debug([
            "hand_shake",
            $socket,
            $ip,
            $port
        ]);
        // 向客戶端發(fā)送握手成功消息,以觸發(fā)客戶端發(fā)送用戶名動(dòng)作;
        $msg = [
            "type" => "handshake",
            "content" => "done",
        ];
        $msg = $this->build(json_encode($msg));
        socket_write($socket, $msg, strlen($msg));
        return true;
    }
    /**
     * 解析數(shù)據(jù)
     *
     * @param $buffer
     *
     * @return bool|string
     */
    private function parse($buffer) {
        $decoded = "";
        $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++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return json_decode($decoded, true);
    }
    /**
     * 將普通信息組裝成websocket數(shù)據(jù)幀
     *
     * @param $msg
     *
     * @return string
     */
    private function build($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);
    }
    /**
     * 拼裝信息
     *
     * @param $socket
     * @param $recv_msg
     *          [
     *          "type"=>user/login
     *          "content"=>content
     *          ]
     *
     * @return string
     */
    private function dealMsg($socket, $recv_msg) {
        $msg_type = $recv_msg["type"];
        $msg_content = $recv_msg["content"];
        $response = [];
        switch ($msg_type) {
            case "login":
                $this->sockets[(int)$socket]["uname"] = $msg_content;
                // 取得最新的名字記錄
                $user_list = array_column($this->sockets, "uname");
                $response["type"] = "login";
                $response["content"] = $msg_content;
                $response["user_list"] = $user_list;
                break;
            case "logout":
                $user_list = array_column($this->sockets, "uname");
                $response["type"] = "logout";
                $response["content"] = $msg_content;
                $response["user_list"] = $user_list;
                break;
            case "user":
                $uname = $this->sockets[(int)$socket]["uname"];
                $response["type"] = "user";
                $response["from"] = $uname;
                $response["content"] = $msg_content;
                break;
        }
        return $this->build(json_encode($response));
    }
    /**
     * 廣播消息
     *
     * @param $data
     */
    private function broadcast($data) {
        foreach ($this->sockets as $socket) {
            if ($socket["resource"] == $this->master) {
                continue;
            }
            socket_write($socket["resource"], $data, strlen($data));
        }
    }
    /**
     * 記錄debug信息
     *
     * @param array $info
     */
    private function debug(array $info) {
        $time = date("Y-m-d H:i:s");
        array_unshift($info, $time);
        $info = array_map("json_encode", $info);
        file_put_contents(self::LOG_PATH . "websocket_debug.log", implode(" | ", $info) . "
", FILE_APPEND);
    }
    /**
     * 記錄錯(cuò)誤信息
     *
     * @param array $info
     */
    private function error(array $info) {
        $time = date("Y-m-d H:i:s");
        array_unshift($info, $time);
        $info = array_map("json_encode", $info);
        file_put_contents(self::LOG_PATH . "websocket_error.log", implode(" | ", $info) . "
", FILE_APPEND);
    }
}
$ws = new WebSocket("127.0.0.1", "8080");

HTML部分




    
    
    


websocket聊天室

當(dāng)前在線:0



文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/108327.html

相關(guān)文章

  • PHP websocket天室實(shí)現(xiàn)

    摘要:在服務(wù)器編程中函數(shù)把進(jìn)程變?yōu)橐粋€(gè)服務(wù)器,并指定相應(yīng)的套接字變?yōu)楸粍?dòng)連接其中的能存儲(chǔ)的請(qǐng)求不明的數(shù)目。 PHP部分

    wangbjun 評(píng)論0 收藏0
  • PHP websocket天室實(shí)現(xiàn)

    摘要:在服務(wù)器編程中函數(shù)把進(jìn)程變?yōu)橐粋€(gè)服務(wù)器,并指定相應(yīng)的套接字變?yōu)楸粍?dòng)連接其中的能存儲(chǔ)的請(qǐng)求不明的數(shù)目。 PHP部分

    mo0n1andin 評(píng)論0 收藏0
  • Websocket解析及實(shí)現(xiàn)

    摘要:早期的輪詢是通過(guò)不斷自動(dòng)刷新頁(yè)面而實(shí)現(xiàn)的。長(zhǎng)輪詢的另一個(gè)問(wèn)題是缺乏標(biāo)準(zhǔn)實(shí)現(xiàn)。服務(wù)器端接到這個(gè)請(qǐng)求后作出回應(yīng)并不斷更新連接狀態(tài)以保證客戶端和服務(wù)器端的連接不過(guò)期。協(xié)議解析協(xié)議包含兩部分一部分是握手,一部分是數(shù)據(jù)傳輸。 Websocket是什么? Websocket是一個(gè)因?yàn)閼?yīng)用場(chǎng)景越來(lái)越復(fù)雜而提出的,針對(duì)瀏覽器和web服務(wù)器之間雙向持續(xù)通信而設(shè)計(jì),而且優(yōu)雅地兼容HTTP的協(xié)議(我猜想:同...

    XboxYan 評(píng)論0 收藏0
  • 實(shí)戰(zhàn) swoole【天室

    摘要:是一個(gè)請(qǐng)求對(duì)象,包含了客戶端發(fā)來(lái)的握手請(qǐng)求信息事件函數(shù)中可以調(diào)用向客戶端發(fā)送數(shù)據(jù)或者調(diào)用關(guān)閉連接事件回調(diào)是可選的當(dāng)服務(wù)器收到來(lái)自客戶端的數(shù)據(jù)幀時(shí)會(huì)回調(diào)此函數(shù)。 前言:了解概念之后就應(yīng)該練練手啦,不然就是巨嬰 有收獲的話請(qǐng)加顆小星星,沒(méi)有收獲的話可以 反對(duì) 沒(méi)有幫助 舉報(bào)三連 代碼倉(cāng)庫(kù) 實(shí)戰(zhàn)swoole【聊天室】 在線體驗(yàn) 準(zhǔn)備工作 需要先看初識(shí)swoole【上】,了解基本的服務(wù)端...

    andycall 評(píng)論0 收藏0
  • 簡(jiǎn)單又好用的天室技術(shù)——WebSocket

    摘要:國(guó)際慣例,先上維基百科的解釋。維基百科上面是維基百科對(duì)的解釋,別問(wèn)我如何解釋上面這段話,因?yàn)槲乙矝](méi)看懂,那么下面我用人話解釋一下吧僅僅是我的理解是一個(gè)協(xié)議,可以簡(jiǎn)單看成是協(xié)議的一個(gè)補(bǔ)充協(xié)議,借助協(xié)議的基礎(chǔ)完成服務(wù)器主動(dòng)與客戶端實(shí)時(shí)傳輸數(shù)據(jù)。 現(xiàn)在,很多網(wǎng)站為了實(shí)現(xiàn)推送技術(shù),所用的技術(shù)都是輪詢。輪詢是在特定的的時(shí)間間隔(如每1秒),由瀏覽器對(duì)服務(wù)器發(fā)出HTTP request,然后由服務(wù)...

    Prasanta 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<