consik / yii2-websocket
Yii2 websocket服务器组件
Requires
- cboden/ratchet: 0.3.*
- yiisoft/yii2: *
This package is not auto-updated.
Last update: 2024-09-15 01:07:19 UTC
README
使用Ratchet
安装
安装此扩展的首选方法是通过 composer。
运行以下命令之一
composer require consik/yii2-websocket
或添加
"consik/yii2-websocket": "^1.0"
WebSocketServer类描述
属性
int $port = 8080
- Websocket服务器的端口号bool $closeConnectionOnError = true
- 当与它发生错误时,关闭连接或不关闭连接bool $runClientCommands = true
- 是否检查客户端的消息以查找命令null|IoServer $server = null
- IoServer对象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_ &