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

資訊專欄INFORMATION COLUMN

實(shí)戰(zhàn) swoole【聊天室】

andycall / 2373人閱讀

摘要:是一個(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)加顆小星星,沒有收獲的話可以 反對(duì) 沒有幫助 舉報(bào)三連

代碼倉(cāng)庫(kù)

實(shí)戰(zhàn)swoole【聊天室】

在線體驗(yàn)

準(zhǔn)備工作

需要先看初識(shí)swoole【上】,了解基本的服務(wù)端WebSocket使用

js WebSocket客戶端簡(jiǎn)單使用

使用
# 命令行1
php src/websocket/run.php
# 命令行2
cd public && php -S localhost:8000
# 客戶端,多開幾個(gè)查看效果
訪問http://localhost:8000/
WebSocket

官方示例

$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}
";
    });
$server->on("message", function (swoole_websocket_server $server, $frame) {
        echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}
";
        $server->push($frame->fd, "this is server");
    });
$server->on("close", function ($ser, $fd) {
        echo "client {$fd} closed
";
    });
$server->on("request", function (swoole_http_request $request, swoole_http_response $response) {
        global $server;//調(diào)用外部的server
        // $server->connections 遍歷所有websocket連接用戶的fd,給所有用戶推送
        foreach ($server->connections as $fd) {
            $server->push($fd, $request->get["message"]);
        }
    });
$server->start();

詳解:

swoole_websocket_server 繼承自 swoole_http_server

設(shè)置了onRequest回調(diào),websocket服務(wù)器也可以同時(shí)作為http服務(wù)器

未設(shè)置onRequest回調(diào),websocket服務(wù)器收到http請(qǐng)求后會(huì)返回http 400錯(cuò)誤頁(yè)面

如果想通過(guò)接收http觸發(fā)所有websocket的推送,需要注意作用域的問題,面向過(guò)程請(qǐng)使用global對(duì)swoole_websocket_server進(jìn)行引用,面向?qū)ο罂梢园裺woole_websocket_server設(shè)置成一個(gè)成員屬性

function onOpen(swoole_websocket_server $svr, swoole_http_request $req);

當(dāng)WebSocket客戶端與服務(wù)器建立連接并完成握手后會(huì)回調(diào)此函數(shù)。

$req 是一個(gè)Http請(qǐng)求對(duì)象,包含了客戶端發(fā)來(lái)的握手請(qǐng)求信息

onOpen事件函數(shù)中可以調(diào)用push向客戶端發(fā)送數(shù)據(jù)或者調(diào)用close關(guān)閉連接

onOpen事件回調(diào)是可選的

function onMessage(swoole_websocket_server $server, swoole_websocket_frame $frame)

當(dāng)服務(wù)器收到來(lái)自客戶端的數(shù)據(jù)幀時(shí)會(huì)回調(diào)此函數(shù)。

$frame 是swoole_websocket_frame對(duì)象,包含了客戶端發(fā)來(lái)的數(shù)據(jù)幀信息

onMessage回調(diào)必須被設(shè)置,未設(shè)置服務(wù)器將無(wú)法啟動(dòng)

客戶端發(fā)送的ping幀不會(huì)觸發(fā)onMessage,底層會(huì)自動(dòng)回復(fù)pong包

swoole_websocket_frame 屬性

$frame->fd,客戶端的socket id,使用$server->push推送數(shù)據(jù)時(shí)需要用到

$frame->data,數(shù)據(jù)內(nèi)容,可以是文本內(nèi)容也可以是二進(jìn)制數(shù)據(jù),可以通過(guò)opcode的值來(lái)判斷

$frame->opcode,WebSocket的OpCode類型,可以參考WebSocket協(xié)議標(biāo)準(zhǔn)文檔

$frame->finish, 表示數(shù)據(jù)幀是否完整,一個(gè)WebSocket請(qǐng)求可能會(huì)分成多個(gè)數(shù)據(jù)幀進(jìn)行發(fā)送(底層已經(jīng)實(shí)現(xiàn)了自動(dòng)合并數(shù)據(jù)幀,現(xiàn)在不用擔(dān)心接收到的數(shù)據(jù)幀不完整)

聊天室服務(wù)端示例

目錄結(jié)構(gòu):

config

socket.php

src

websocket

Config.php

run.php

WebSocketServer.php 內(nèi)存表版本

WsRedisServer.php redis版本

WebSocketServer.php 內(nèi)存表版本

createTable();
        // 實(shí)例化配置
        $this->config = Config::getInstance();
    }

    public function run()
    {
        $this->server = new swoole_websocket_server(
            $this->config["socket"]["host"],
            $this->config["socket"]["port"]
        );

        $this->server->on("open", [$this, "open"]);
        $this->server->on("message", [$this, "message"]);
        $this->server->on("close", [$this, "close"]);

        $this->server->start();
    }

    public function open(swoole_websocket_server $server, swoole_http_request $request)
    {
        $user = [
            "fd" => $request->fd,
            "name" => $this->config["socket"]["name"][array_rand($this->config["socket"]["name"])] . $request->fd,
            "avatar" => $this->config["socket"]["avatar"][array_rand($this->config["socket"]["avatar"])]
        ];
        // 放入內(nèi)存表
        $this->table->set($request->fd, $user);

        $server->push($request->fd, json_encode(
                array_merge(["user" => $user], ["all" => $this->allUser()], ["type" => "openSuccess"])
            )
        );
    }

    private function allUser()
    {
        $users = [];
        foreach ($this->table as $row) {
            $users[] = $row;
        }
        return $users;
    }

    public function message(swoole_websocket_server $server, swoole_websocket_frame $frame)
    {
        $this->pushMessage($server, $frame->data, "message", $frame->fd);
    }

    /**
     * 推送消息
     *
     * @param swoole_websocket_server $server
     * @param string $message
     * @param string $type
     * @param int $fd
     */
    private function pushMessage(swoole_websocket_server $server, string $message, string $type, int $fd)
    {
        $message = htmlspecialchars($message);
        $datetime = date("Y-m-d H:i:s", time());
        $user = $this->table->get($fd);

        foreach ($this->table as $item) {
            // 自己不用發(fā)送
            if ($item["fd"] == $fd) {
                continue;
            }

            $server->push($item["fd"], json_encode([
                "type" => $type,
                "message" => $message,
                "datetime" => $datetime,
                "user" => $user
            ]));
        }
    }

    /**
     * 客戶端關(guān)閉的時(shí)候
     *
     * @param swoole_websocket_server $server
     * @param int $fd
     */
    public function close(swoole_websocket_server $server, int $fd)
    {
        $user = $this->table->get($fd);
        $this->pushMessage($server, "{$user["name"]}離開聊天室", "close", $fd);
        $this->table->del($fd);
    }

    /**
     * 創(chuàng)建內(nèi)存表
     */
    private function createTable()
    {
        $this->table = new swoole_table(1024);
        $this->table->column("fd", swoole_table::TYPE_INT);
        $this->table->column("name", swoole_table::TYPE_STRING, 255);
        $this->table->column("avatar", swoole_table::TYPE_STRING, 255);
        $this->table->create();
    }
}

WsRedisServer.php redis版本

config = Config::getInstance();
        // redis
        $this->initRedis();
        // 初始化,主要是服務(wù)端自己關(guān)閉不會(huì)清空redis
        foreach ($this->allUser() as $item) {
            $this->client->hdel("{$this->key}:{$item["fd"]}", ["fd", "name", "avatar"]);
        }
    }

    public function run()
    {
        $this->server = new swoole_websocket_server(
            $this->config["socket"]["host"],
            $this->config["socket"]["port"]
        );

        $this->server->on("open", [$this, "open"]);
        $this->server->on("message", [$this, "message"]);
        $this->server->on("close", [$this, "close"]);

        $this->server->start();
    }

    public function open(swoole_websocket_server $server, swoole_http_request $request)
    {
        $user = [
            "fd" => $request->fd,
            "name" => $this->config["socket"]["name"][array_rand($this->config["socket"]["name"])] . $request->fd,
            "avatar" => $this->config["socket"]["avatar"][array_rand($this->config["socket"]["avatar"])]
        ];
        // 放入redis
        $this->client->hmset("{$this->key}:{$user["fd"]}", $user);

        // 給每個(gè)人推送,包括自己
        foreach ($this->allUser() as $item) {
            $server->push($item["fd"], json_encode([
                "user" => $user,
                "all" => $this->allUser(),
                "type" => "openSuccess"
            ]));
        }
    }

    private function allUser()
    {
        $users = [];
        $keys = $this->client->keys("{$this->key}:*");
        // 所有的key
        foreach ($keys as $k => $item) {
            $users[$k]["fd"] = $this->client->hget($item, "fd");
            $users[$k]["name"] = $this->client->hget($item, "name");
            $users[$k]["avatar"] = $this->client->hget($item, "avatar");
        }
        return $users;
    }

    public function message(swoole_websocket_server $server, swoole_websocket_frame $frame)
    {
        $this->pushMessage($server, $frame->data, "message", $frame->fd);
    }

    /**
     * 推送消息
     *
     * @param swoole_websocket_server $server
     * @param string $message
     * @param string $type
     * @param int $fd
     */
    private function pushMessage(swoole_websocket_server $server, string $message, string $type, int $fd)
    {
        $message = htmlspecialchars($message);
        $datetime = date("Y-m-d H:i:s", time());
        $user["fd"] = $this->client->hget("{$this->key}:{$fd}", "fd");
        $user["name"] = $this->client->hget("{$this->key}:{$fd}", "name");
        $user["avatar"] = $this->client->hget("{$this->key}:{$fd}", "avatar");

        foreach ($this->allUser() as $item) {
            // 自己不用發(fā)送
            if ($item["fd"] == $fd) {
                continue;
            }

            $is_push = $server->push($item["fd"], json_encode([
                "type" => $type,
                "message" => $message,
                "datetime" => $datetime,
                "user" => $user
            ]));
            // 刪除失敗的推送
            if (!$is_push) {
                $this->client->hdel("{$this->key}:{$item["fd"]}", ["fd", "name", "avatar"]);
            }
        }
    }

    /**
     * 客戶端關(guān)閉的時(shí)候
     *
     * @param swoole_websocket_server $server
     * @param int $fd
     */
    public function close(swoole_websocket_server $server, int $fd)
    {
        $user["fd"] = $this->client->hget("{$this->key}:{$fd}", "fd");
        $user["name"] = $this->client->hget("{$this->key}:{$fd}", "name");
        $user["avatar"] = $this->client->hget("{$this->key}:{$fd}", "avatar");
        $this->pushMessage($server, "{$user["name"]}離開聊天室", "close", $fd);
        $this->client->hdel("{$this->key}:{$fd}", ["fd", "name", "avatar"]);
    }

    /**
     * 初始化redis
     */
    private function initRedis()
    {
        $this->client = new Client([
            "scheme" => $this->config["socket"]["redis"]["scheme"],
            "host" => $this->config["socket"]["redis"]["host"],
            "port" => $this->config["socket"]["redis"]["port"],
        ]);
    }
}

config.php

path = __DIR__ . "/../../config/";
    }

    // 單例模式
    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function offsetSet($offset, $value)
    {
        // 閹割
    }

    public function offsetGet($offset)
    {
        if (empty($this->config)) {
            $this->config[$offset] = require $this->path . $offset . ".php";
        }
        return $this->config[$offset];
    }

    public function offsetExists($offset)
    {
        return isset($this->config[$offset]);
    }

    public function offsetUnset($offset)
    {
        // 閹割
    }

    // 禁止克隆
    final private function __clone(){}
}

config/socket.php

 "0.0.0.0",
    "port" => 9501,

    "redis" => [
        "scheme" => "tcp",
        "host" => "0.0.0.0",
        "port" => 6380
    ],

    "avatar" => [
        "./images/avatar/1.jpg",
        "./images/avatar/2.jpg",
        "./images/avatar/3.jpg",
        "./images/avatar/4.jpg",
        "./images/avatar/5.jpg",
        "./images/avatar/6.jpg"
    ],

    "name" => [
        "科比",
        "庫(kù)里",
        "KD",
        "KG",
        "喬丹",
        "鄧肯",
        "格林",
        "湯普森",
        "伊戈達(dá)拉",
        "麥迪",
        "艾弗森",
        "卡哇伊",
        "保羅"
    ]
];

run.php

run();
總結(jié)

完整示例:聊天室

學(xué)完后發(fā)現(xiàn)生活中所謂的聊天室其實(shí)也不過(guò)如此,當(dāng)然這只是簡(jiǎn)單的demo,很多功能都沒有實(shí)現(xiàn),想進(jìn)一步學(xué)習(xí)的話可以去github上找完整的項(xiàng)目進(jìn)行深入學(xué)習(xí)

參考

swoole

PHP + Swoole 實(shí)現(xiàn)的簡(jiǎn)單聊天室

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

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

相關(guān)文章

  • swoole 超簡(jiǎn)單 構(gòu)建天室.資辭 群聊,組聊,單聊.

    摘要:今天來(lái)做一個(gè)簡(jiǎn)單的聊天室支持換房間支持私信的寫的代碼有點(diǎn)渣里面有很多不是很好的地方畢竟我只是一個(gè)野生程序猿環(huán)境地址樣子差不多是這個(gè)樣子的我不想把代碼發(fā)到我的服務(wù)器上因?yàn)檫@個(gè)項(xiàng)目太小了很垃圾而且怕被攻擊這里有錄的一個(gè)演示視頻沒有廣告的你們可以 今天來(lái)做一個(gè)簡(jiǎn)單的聊天室,支持換房間,支持私信的. 寫的代碼有點(diǎn)渣,里面有很多不是很好的地方.畢竟我只是一個(gè)野生程序猿. 環(huán)境: php7.0...

    Towers 評(píng)論0 收藏0
  • swoole 服務(wù)端120行代碼構(gòu)建一個(gè)websocket 天室.

    摘要:的異步并行高性能網(wǎng)絡(luò)通信引擎,使用純語(yǔ)言編寫,提供了語(yǔ)言的異步多線程服務(wù)器,異步網(wǎng)絡(luò)客戶端,異步,異步,數(shù)據(jù)庫(kù)連接池,,消息隊(duì)列,毫秒定時(shí)器,異步文件讀寫,異步查詢。內(nèi)置了服務(wù)器端客戶端服務(wù)器端。 swoole :http://www.swoole.com/PHP的異步、并行、高性能網(wǎng)絡(luò)通信引擎,使用純C語(yǔ)言編寫,提供了PHP語(yǔ)言的異步多線程服務(wù)器,異步TCP/UDP網(wǎng)絡(luò)客戶端,異步M...

    韓冰 評(píng)論0 收藏0
  • swoole簡(jiǎn)單的天室demo(修正版)

    摘要:搜了一下,以前的很多類型文章,都是可能采集的,基本一樣,錯(cuò)誤都一樣,所以自己寫了個(gè),發(fā)來(lái)共享一下。 搜了一下,以前的很多類型文章,都是可能采集的,基本一樣,錯(cuò)誤都一樣,所以自己寫了個(gè),發(fā)來(lái)共享一下。 咱們可是抄襲文檔還有互聯(lián)網(wǎng)的,請(qǐng)確保你的服務(wù)器已經(jīng)安裝swoole1.7.7+版本擴(kuò)展,9502端口未占用而且可以開啟 swoole文檔:https://wiki.swoole.com/w...

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

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

0條評(píng)論

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