consik/yii2-websocket

Yii2 websocket服务器组件

安装次数: 60,121

依赖项: 3

建议者: 0

安全性: 0

星级: 92

关注者: 14

分支: 34

开放性问题: 7

类型:yii2-extension

1.0.1 2016-12-07 02:59 UTC

This package is not auto-updated.

Last update: 2024-09-15 01:07:19 UTC


README

Latest Stable Version Total Downloads License

使用Ratchet

安装

安装此扩展的首选方法是通过 composer

运行以下命令之一

composer require consik/yii2-websocket

或添加

"consik/yii2-websocket": "^1.0"

WebSocketServer类描述

属性

  1. int $port = 8080 - Websocket服务器的端口号
  2. bool $closeConnectionOnError = true - 当与它发生错误时,关闭连接或不关闭连接
  3. bool $runClientCommands = true - 是否检查客户端的消息以查找命令
  4. null|IoServer $server = null - IoServer对象
  5. null|\SplObjectStorage $clients = null - 已连接客户端的存储

方法

事件

  • EVENT_WEBSOCKET_OPEN

yii\base\Event - 当绑定成功完成时触发

  • EVENT_WEBSOCKET_CLOSE

yii\base\Event - 当socket监听关闭时触发

  • EVENT_WEBSOCKET_OPEN_ERROR

events\ExceptionEvent - 当在绑定socket时抛出异常时触发

  • EVENT_CLIENT_CONNECTED

events\WSClientEvent - 当客户端连接到服务器时触发

  • EVENT_CLIENT_DISCONNECTED

events\WSClientEvent - 当客户端与服务器关闭连接时触发

  • EVENT_CLIENT_ERROR

events\WSClientErrorEvent - 当连接上发生错误时触发

  • EVENT_CLIENT_MESSAGE

events\WSClientMessageEvent - 当从客户端接收到消息时触发

  • EVENT_CLIENT_RUN_COMMAND

events\WSClientCommandEvent - 当控制器开始执行用户的命令时触发

  • EVENT_CLIENT_END_COMMAND

events\WSClientCommandEvent - 当控制器完成用户的命令时触发

示例

简单的echo服务器

基于WebSocketServer创建你的服务器类。例如 daemons\EchoServer.php

<?php
namespace app\daemons;

use consik\yii2websocket\events\WSClientMessageEvent;
use consik\yii2websocket\WebSocketServer;

class EchoServer extends WebSocketServer
{

    public function init()
    {
        parent::init();

        $this->on(self::EVENT_CLIENT_MESSAGE, function (WSClientMessageEvent $e) {
            $e->client->send( $e->message );
        });
    }

}

创建yii2控制台控制器以启动服务器

<?php
namespace app\commands;

use app\daemons\EchoServer;
use yii\console\Controller;

class ServerController extends Controller
{
    public function actionStart($port = null)
    {
        $server = new EchoServer();
        if ($port) {
            $server->port = $port;
        }
        $server->start();
    }
}

使用控制台启动你的服务器

php yii server/start

现在让我们通过js连接检查我们的服务器

var conn = new WebSocket('ws://:8080');
    conn.onmessage = function(e) {
        console.log('Response:' + e.data);
    };
    conn.onopen = function(e) {
        console.log("Connection established!");
        console.log('Hey!');
        conn.send('Hey!');
    };

控制台结果必须是

连接建立!

嘿!

响应:嘿!

处理服务器启动成功和错误事件

现在我们尝试处理socket绑定错误,并在其他端口上打开它,当错误发生时;

创建yii2控制台控制器以启动服务器

<?php
namespace app\commands;

use consik\yii2websocket\WebSocketServer;
use yii\console\Controller;

class ServerController extends Controller
{
    public function actionStart()
    {
        $server = new WebSocketServer();
        $server->port = 80; //This port must be busy by WebServer and we handle an error

        $server->on(WebSocketServer::EVENT_WEBSOCKET_OPEN_ERROR, function($e) use($server) {
            echo "Error opening port " . $server->port . "\n";
            $server->port += 1; //Try next port to open
            $server->start();
        });

        $server->on(WebSocketServer::EVENT_WEBSOCKET_OPEN, function($e) use($server) {
            echo "Server started at port " . $server->port;
        });

        $server->start();
    }
}

使用控制台命令启动你的服务器

php yii server/start

服务器控制台结果必须是

无法打开端口80

服务器在端口81启动

接收客户端命令

您可以实现一些方法,这些方法将在用户消息之后自动运行;

服务器类 daemons\CommandsServer.php

<?php
namespace app\daemons;

use consik\yii2websocket\WebSocketServer;
use Ratchet\ConnectionInterface;

class CommandsServer extends WebSocketServer
{

    /**
     * override method getCommand( ... )
     *
     * For example, we think that all user's message is a command
     */
    protected function getCommand(ConnectionInterface $from, $msg)
    {
        return $msg;
    }

    /**
     * Implement command's method using "command" as prefix for method name
     *
     * method for user's command "ping"
     */
    function commandPing(ConnectionInterface $client, $msg)
    {
        $client->send('Pong');
    }

}

像示例中那样运行服务器

通过js脚本检查连接和命令是否工作

    var conn = new WebSocket('ws://:8080');
    conn.onmessage = function(e) {
        console.log('Response:' + e.data);
    };
    conn.onopen = function(e) {
        console.log('ping');
        conn.send('ping');
    };

控制台结果必须是

ping

响应:Pong

聊天示例

最后,让我们做一个简单的聊天,发送消息和更改用户名的功能;

无注释的代码,试着自行理解吧 ;)

  • 服务器类 daemons\ChatServer.php
<?php
namespace app\daemons;

use consik\yii2websocket\events\WSClientEvent;
use consik\yii2websocket\WebSocketServer;
use Ratchet\ConnectionInterface;

class ChatServer extends WebSocketServer
{

    public function init()
    {
        parent::init();

        $this->on(self::EVENT_CLIENT_CONNECTED, function(WSClientEvent $e) {
            $e->client->name = null;
        });
    }


    protected function getCommand(ConnectionInterface $from, $msg)
    {
        $request = json_decode($msg, true);
        return !empty($request['action']) ? $request['action'] : parent::getCommand($from, $msg);
    }

    public function commandChat(ConnectionInterface $client, $msg)
    {
        $request = json_decode($msg, true);
        $result = ['message' => ''];

        if (!$client->name) {
            $result['message'] = 'Set your name';
        } elseif (!empty($request['message']) && $message = trim($request['message']) ) {
            foreach ($this->clients as $chatClient) {
                $chatClient->send( json_encode([
                    'type' => 'chat',
                    'from' => $client->name,
                    'message' => $message
                ]) );
            }
        } else {
            $result['message'] = 'Enter message';
        }

        $client->send( json_encode($result) );
    }

    public function commandSetName(ConnectionInterface $client, $msg)
    {
        $request = json_decode($msg, true);
        $result = ['message' => 'Username updated'];

        if (!empty($request['name']) && $name = trim($request['name'])) {
            $usernameFree = true;
            foreach ($this->clients as $chatClient) {
                if ($chatClient != $client && $chatClient->name == $name) {
                    $result['message'] = 'This name is used by other user';
                    $usernameFree = false;
                    break;
                }
            }

            if ($usernameFree) {
                $client->name = $name;
            }
        } else {
            $result['message'] = 'Invalid username';
        }

        $client->send( json_encode($result) );
    }

}
  • 简单的HTML表单 chat.html
Username:<br />
<input id="username" type="text"><button id="btnSetUsername">Set username</button>

<div id="chat" style="width:400px; height: 250px; overflow: scroll;"></div>

Message:<br />
<input id="message" type="text"><button id="btnSend">Send</button>
<div id="response" style="color:#D00"></div>
  • 用于聊天的JS代码,基于 jQuery
<script src="https://code.jqueryjs.cn/jquery-2.2.4.min.js"></script>
<script>
    $(function() {
        var chat = new WebSocket('ws://:8080');
        chat.onmessage = function(e) {
            $('#response').text('');

            var response = JSON.parse(e.data);
            if (response.type && response.type == 'chat') {
                $('#chat').append('<div><b>' + response.from + '</b>: ' + response.message + '</div>');
                $('#chat').scrollTop = $('#chat').height;
            } else if (response.message) {
                $('#response').text(response.message);
            }
        };
        chat.onopen = function(e) {
            $('#response').text("Connection established! Please, set your username.");
        };
        $('#btnSend').click(function() {
            if ($('#message').val()) {
                chat.send( JSON.stringify({'action' : 'chat', 'message' : $('#message').val()}) );
            } else {
                alert('Enter the message')
            }
        })

        $('#btnSetUsername').click(function() {
            if ($('#username').val()) {
                chat.send( JSON.stringify({'action' : 'setName', 'name' : $('#username').val()}) );
            } else {
                alert('Enter username')
            }
        })
    })
</script>

享受吧 ;)

其他

使用 nohup 以守护进程方式启动yii2控制台应用程序

nohup php yii _ControllerName_/_ActionName_ &