swoole 的练习 demo(3)- 简陋的 websocket 项目

swoole 的练习 demo(3)- 简陋的 websocket 项目

一直不能下决心好好学习,仔细研究一下,决定用尽量降低难度曲线的方法,从易到难,一步一步的学习,所以整了个demo项目。

git仓库和使用步骤

确保能看到swoole,在 php -m 命令中
php -m 
git clone https://github.com/lang123789/swoole_demo.git
然后设置了标签,本文对应 v3.0
cd swoole_demo
git checkout v3.0

有提示错误之类,但代码已经切换到 v3.0 了。

## 安装类库,生成 autoload.php 文件
composer install
## 启动 websocket 服务
php src/WebSocket/run.php

需求

4、任意一个前端页面上线,则服务器后台打印“有人上线了,用户id,用户名称”。
5、如果前端页面关闭,就是离线了,则服务器后台打印有人离线。

测试网址

127.0.0.1/index.php?id=1
127.0.0.1/index.php?id=2

主要代码

客户端主要代码

客户端代码主要在 index.php 中

window.onload = function(){
     activate_chat_js();
     var wsServer = 'ws://{$JS_IP}:9501';
     var websocket = new WebSocket(wsServer); 
     websocket.onopen = function (evt) { 
         console.log("Connected to WebSocket server.");
         websocket.send("my_id|{$user_id}");
     };

    websocket.onclose = function (evt) { 
        console.log("Disconnected");
    };
    websocket.onmessage = function (evt) { 
       console.log('Retrieved data from server: ' + evt.data);
       var user = JSON.parse(evt.data);
       if (user.type=='my_id'){
            system_chatWindow(user.message);
       }
    };

    websocket.onerror = function (evt, e) { 
       console.log('Error occured: ' + evt.data);
    };    
}

服务端主要代码

class WebSocketServer
{
  private $config;
  private $table;
  private $server;

  private $user_all;

  public function __construct() {
    // 实例化配置
    // 内存表 实现进程间共享数据,也可以使用redis替代
    $this->createTable();
    $this->config = Config::getInstance();
    $this->user_all = $this->config['socket']['user_all'];
  }

  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) {  
     echo "有人上线\n";
  }

  public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame) {
     $data = $frame->data;
     //这里用户发来的信息已经分类型了。my_id开头,说明是系统自动从客户端发送的信息,用于识别身份。

     if (preg_match( '#my_id|#', $data )) {
       $user_id= explode("|",$data)[1] ;
       $arr=$this->user_all;
       $user_name = isset( $arr[$user_id] )? $arr[$user_id] :'';
       if (!$user_name){
          //如果用户不存在,则w我直接关闭连接。
          echo "非法连接。用户id:".$user_id;
          $server->close($frame->fd);
          return;
       }

       $user = [
          'fd' => $frame->fd,
          'user_name' => $user_name,
          'user_id' => strval($user_id),
       ];
       echo "有个人刚上线,数据:".json_encode( $user, JSON_UNESCAPED_UNICODE );
       // 放入内存表
       $this->table->set($frame->fd, $user);

       $server->push($frame->fd, json_encode([
          'type' => 'my_id',
          'message' => '欢迎您,'.$user_name,
       ]));
    }

 }

  /**
    * 客户端关闭的时候
   */  
   public function close(\swoole_websocket_server $server, int $fd) {  
      $user = $this->table->get($fd);
      echo "有人下线,数据:".json_encode( $user, JSON_UNESCAPED_UNICODE );
      $this->table->del($fd);
  }

  /**
    * 创建内存表
   */
  private function createTable() {  
    $this->table = new \swoole_table(1024);
    $this->table->column('fd', \swoole_table::TYPE_INT);
    $this->table->column('user_name', \swoole_table::TYPE_STRING, 255);
    $this->table->column('user_id', \swoole_table::TYPE_STRING, 255);
    $this->table->create();
  }

目录 config 下的 socket.php 是配置文件,决定了服务器在哪个端口启动 websocket 服务。
以及面向哪些ip开放。
WebSocket 目录下的 Config.php 是对 config文件做的封装,用处不大。
WebSocketServer.php 是核心文件。

代码说明

客户端首页用php取代html,这样可以填写用户id。
php代码接受请求参数id,代表了用户id。
用数组代替查数据库,实现功能。
实现了简陋的 websocket 连接功能。

流程和原理说明

(1)我们在服务器的 shell 执行 php src/WebSocket/run.php,shell被同步阻塞。
(2)浏览器用 http 协议请求首页 http://127.0.0.1/index.php?id=1,参数带了用户id
(3)服务器先确认用户是否存在,然后返回首页。
(4)在浏览器这里,var websocket = new WebSocket(wsServer); 这句话被执行。
(5)浏览器用 ws 协议请求与服务器长连接,服务器接受了,令人喜悦的 websocket 长连接开始。
(6)双方都连接上后,服务端先执行 WebSocketServer 类 的 open回调方法
(7)客户端因为open成功,执行回调,websocket.send(“my_id|{$user_id}”); 被执行。
(8)服务端执行 WebSocketServer 类 的 message 回调方法
(9)服务端检查是否特定开头,来判定用户端到底想做什么,my_id开头表示用户自我介绍身份。
(10)查用户id是否存在,如果存在,保存到swoole_table, 且向用户推送特定格式的消息。欢迎您,某某某。
(11)客户端的 onmessage 执行,显示在对话框中,欢迎您,某某某。
(12)假设用户离开,关闭浏览器的窗口,则服务端的close回调被执行。用户信息从swoole_table中删除

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
49
粉丝
7
喜欢
60
收藏
94
排名:592
访问:1.3 万
私信
所有博文
社区赞助商