摘要:是一個(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
摘要:今天來(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...
摘要:的異步并行高性能網(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...
摘要:搜了一下,以前的很多類型文章,都是可能采集的,基本一樣,錯(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...
閱讀 1223·2021-09-03 10:44
閱讀 618·2019-08-30 13:13
閱讀 2813·2019-08-30 13:11
閱讀 1977·2019-08-30 12:59
閱讀 1049·2019-08-29 15:32
閱讀 1611·2019-08-29 15:25
閱讀 1006·2019-08-29 12:24
閱讀 1294·2019-08-27 10:58