摘要:是一個請求對象,包含了客戶端發來的握手請求信息事件函數中可以調用向客戶端發送數據或者調用關閉連接事件回調是可選的當服務器收到來自客戶端的數據幀時會回調此函數。
前言:了解概念之后就應該練練手啦,不然就是巨嬰
有收獲的話請加顆小星星,沒有收獲的話可以 反對 沒有幫助 舉報三連
代碼倉庫
實戰swoole【聊天室】
在線體驗
準備工作需要先看初識swoole【上】,了解基本的服務端WebSocket使用
js WebSocket客戶端簡單使用
使用# 命令行1 php src/websocket/run.php # 命令行2 cd public && php -S localhost:8000 # 客戶端,多開幾個查看效果 訪問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;//調用外部的server // $server->connections 遍歷所有websocket連接用戶的fd,給所有用戶推送 foreach ($server->connections as $fd) { $server->push($fd, $request->get["message"]); } }); $server->start();
詳解:
swoole_websocket_server 繼承自 swoole_http_server
設置了onRequest回調,websocket服務器也可以同時作為http服務器
未設置onRequest回調,websocket服務器收到http請求后會返回http 400錯誤頁面
如果想通過接收http觸發所有websocket的推送,需要注意作用域的問題,面向過程請使用global對swoole_websocket_server進行引用,面向對象可以把swoole_websocket_server設置成一個成員屬性
function onOpen(swoole_websocket_server $svr, swoole_http_request $req);
當WebSocket客戶端與服務器建立連接并完成握手后會回調此函數。
$req 是一個Http請求對象,包含了客戶端發來的握手請求信息
onOpen事件函數中可以調用push向客戶端發送數據或者調用close關閉連接
onOpen事件回調是可選的
function onMessage(swoole_websocket_server $server, swoole_websocket_frame $frame)
當服務器收到來自客戶端的數據幀時會回調此函數。
$frame 是swoole_websocket_frame對象,包含了客戶端發來的數據幀信息
onMessage回調必須被設置,未設置服務器將無法啟動
客戶端發送的ping幀不會觸發onMessage,底層會自動回復pong包
swoole_websocket_frame 屬性
$frame->fd,客戶端的socket id,使用$server->push推送數據時需要用到
$frame->data,數據內容,可以是文本內容也可以是二進制數據,可以通過opcode的值來判斷
$frame->opcode,WebSocket的OpCode類型,可以參考WebSocket協議標準文檔
$frame->finish, 表示數據幀是否完整,一個WebSocket請求可能會分成多個數據幀進行發送(底層已經實現了自動合并數據幀,現在不用擔心接收到的數據幀不完整)
聊天室服務端示例目錄結構:
config
socket.php
src
websocket
Config.php
run.php
WebSocketServer.php 內存表版本
WsRedisServer.php redis版本
WebSocketServer.php 內存表版本
createTable(); // 實例化配置 $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"])] ]; // 放入內存表 $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) { // 自己不用發送 if ($item["fd"] == $fd) { continue; } $server->push($item["fd"], json_encode([ "type" => $type, "message" => $message, "datetime" => $datetime, "user" => $user ])); } } /** * 客戶端關閉的時候 * * @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); } /** * 創建內存表 */ 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(); // 初始化,主要是服務端自己關閉不會清空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); // 給每個人推送,包括自己 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) { // 自己不用發送 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"]); } } } /** * 客戶端關閉的時候 * * @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" => [ "科比", "庫里", "KD", "KG", "喬丹", "鄧肯", "格林", "湯普森", "伊戈達拉", "麥迪", "艾弗森", "卡哇伊", "保羅" ] ];
run.php
run();總結
完整示例:聊天室
學完后發現生活中所謂的聊天室其實也不過如此,當然這只是簡單的demo,很多功能都沒有實現,想進一步學習的話可以去github上找完整的項目進行深入學習
參考swoole
PHP + Swoole 實現的簡單聊天室
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29836.html
摘要:今天來做一個簡單的聊天室支持換房間支持私信的寫的代碼有點渣里面有很多不是很好的地方畢竟我只是一個野生程序猿環境地址樣子差不多是這個樣子的我不想把代碼發到我的服務器上因為這個項目太小了很垃圾而且怕被攻擊這里有錄的一個演示視頻沒有廣告的你們可以 今天來做一個簡單的聊天室,支持換房間,支持私信的. 寫的代碼有點渣,里面有很多不是很好的地方.畢竟我只是一個野生程序猿. 環境: php7.0...
摘要:的異步并行高性能網絡通信引擎,使用純語言編寫,提供了語言的異步多線程服務器,異步網絡客戶端,異步,異步,數據庫連接池,,消息隊列,毫秒定時器,異步文件讀寫,異步查詢。內置了服務器端客戶端服務器端。 swoole :http://www.swoole.com/PHP的異步、并行、高性能網絡通信引擎,使用純C語言編寫,提供了PHP語言的異步多線程服務器,異步TCP/UDP網絡客戶端,異步M...
摘要:搜了一下,以前的很多類型文章,都是可能采集的,基本一樣,錯誤都一樣,所以自己寫了個,發來共享一下。 搜了一下,以前的很多類型文章,都是可能采集的,基本一樣,錯誤都一樣,所以自己寫了個,發來共享一下。 咱們可是抄襲文檔還有互聯網的,請確保你的服務器已經安裝swoole1.7.7+版本擴展,9502端口未占用而且可以開啟 swoole文檔:https://wiki.swoole.com/w...
閱讀 1207·2021-09-03 10:44
閱讀 603·2019-08-30 13:13
閱讀 2796·2019-08-30 13:11
閱讀 1967·2019-08-30 12:59
閱讀 1034·2019-08-29 15:32
閱讀 1595·2019-08-29 15:25
閱讀 987·2019-08-29 12:24
閱讀 1277·2019-08-27 10:58