6

环境

  • php5.6+

  • swoole扩展

需求

实现简单的广播, 支持身份验证

源码

废话少说,上代码.

<?php 

namespace church;

use ReflectionClass;
use ReflectionException;
use Swoole\Websocket\Server;

class WebSocket
{
    public $host;

    public $port;

    public $config;

    public $beforeAuthCallback = '';

    public $afterAuthCallback = '';

    public $beforeSendMsgCallback = ''; 

    public $afterSendMsgCallback = '';

    public $authenticator = '';

    public $customers = [];

    protected $server;

    public function __construct($host = '0.0.0.0', $port = 9501, $config = [])
    {
        $this->host = $host;
        $this->port = $port;    
        $this->config = $config;
        $this->init();
    } 

    public function init()
    {
        $this->server = new Server($this->host, $this->port);
        !empty($this->config) && $this->server->set($this->config);

        $this->beforeSendMsgCallback = function() {};
        $this->afterSendMsgCallback = function() {};
        $this->beforeAuthCallback = function() {};
        $this->afterAuthCallback = function() {};
        $this->authenticator = function() {};
        $this->bindEvents();
    }

    public function bindEvents()
    {
        $this->server->on('open', [$this, 'onOpen']);
        $this->server->on('message', [$this, 'onMessage']);
        $this->server->on('close', [$this, 'onClose']);
    }

    public function run()
    {
       $this->server->start(); 
    }
    
    public function onOpen(Server $server, $request)
    {
        call_user_func($this->beforeAuthCallback, $server, $request);
        if (!$this->auth($request)) {
            return;
        }
        $this->customers[] = $request->fd;
        call_user_func($this->afterAuthCallback, $server, $request);
    }

    public function onMessage(Server $server, $frame)
    {
        call_user_func($this->beforeSendMsgCallback, $server, $frame);
        $this->broadcast($frame->fd, $frame->data);
        call_user_func($this->afterSendMsgCallback, $server, $frame);
    }

    public function onClose($ser, $fd)
    {
        $key = array_search($fd, $this->customers);
        unset($this->customers[$key]);
    }

    public function __call($method, $params)
    {
        $class_name = get_class($this->server);
        $class = new ReflectionClass($class_name);

        try {
            $class->getMethod($method);
        } catch (ReflectionException $e) {
            echo "Method $method is not exists\n";
            return;
        }

        call_user_func_array([$this->server, $method], $params);
    }

    public function broadcast($fromUser, $messages)
    {
        $users = array_diff($this->customers, [$fromUser]);
        foreach ($users as $user)
        {
            $this->server->push($user, $messages);
        }
    }

    private function auth($request)
    {
        if (($result = call_user_func($this->authenticator, $request)) === false) {
            $this->server->push($request->fd, "auth failed");
            $this->server->close($request->fd);
        }

        return $result;
    }


}

使用实例

<?php 

require './vendor/autoload.php';

use church\WebSocket;

$server = new WebSocket('0.0.0.0', 9501, [
    'daemonize' => 1,
]);

$server->authenticator = function($request) use ($server) {
    $get = $request->get;
    return empty($get) || ($get['access_token'] == 'access_token');
};

$server->afterAuthCallback = function($ser, $request) use ($server) {
    $server->push($request->fd, "auth successful"); 
};

$server->run();
php index.php  #就会以守护进程方式运行

客户端js代码:

var websocket = new WebSocket("ws://192.168.2.106:9501?access_token=access_token");
websocket.onopen=function(evt) {

};

websocket.onmessage = function(evt) {
    console.log(evt);
};

websocket.onclose = function(evt) {
  console.log(evt);
};

搭建过程非常方便。实现的比较简陋,但是可以满足一般的需求了。支持身份验证功能,验证不通过会自动踢掉。


church
3.6k 声望67 粉丝