自定义WEB SOCKET 命令解析
EasySwoole为了方便用户使用WEB_SOCKET进行开发,同样封装了对Swoole的sock操作。
定义命令解析
namespace App\Model\WebSock;
use Core\Component\Socket\AbstractInterface\AbstractClient;
use Core\Component\Socket\AbstractInterface\AbstractCommandParser;
use Core\Component\Socket\Common\Command;
class Parser extends AbstractCommandParser
{
function parser(Command $result, AbstractClient $client, $rawData)
{
// TODO: Implement parser() method.
//这里的解析规则是与客户端匹配的,等会请看客户端代码
$js = json_decode($rawData,1);
if(is_array($js)){
if(isset($js['action'])){
$result->setCommand($js['action']);
}
if(isset($js['content'])){
$result->setMessage($js['content']);
}
}
}
}
定义命令注册类
namespace App\Model\WebSock;
use Core\Component\Socket\AbstractInterface\AbstractCommandRegister;
use Core\Component\Socket\Client\TcpClient;
use Core\Component\Socket\Common\Command;
use Core\Component\Socket\Common\CommandList;
use Core\Swoole\AsyncTaskManager;
use Core\Swoole\Server;
class Register extends AbstractCommandRegister
{
function register(CommandList $commandList)
{
// TODO: Implement register() method.
$commandList->addCommandHandler('who',function (Command $command,TcpClient $client){
return 'your fd is '.$client->getFd();
});
$commandList->addCommandHandler('sendTo',function (Command $command,TcpClient $client){
$dest = intval($command->getMessage());
$info = Server::getInstance()->getServer()->connection_info($dest);
if($info['websocket_status']){
Server::getInstance()->getServer()->push($dest,'you receive a message from '.$client->getFd());
return 'send success';
}else{
return 'fd error';
}
});
$commandList->addCommandHandler('broadcast',function (Command $command){
/*
* 注意 本example未引入redis来做fd信息记录,因此每次采用遍历的形式来获取结果,
* 仅供思路参考,不建议在生产环节使用
*/
$message = $command->getMessage();
$list = array();
foreach (Server::getInstance()->getServer()->connections as $fd){
$info = Server::getInstance()->getServer()->connection_info($fd);
if($info['websocket_status']){
$list[] = $fd;
}
}
//广播属于重任务,交给Task执行
AsyncTaskManager::getInstance()->add(function ()use ($list,$message){
foreach ( $list as $fd) {
Server::getInstance()->getServer()->push($fd,"this is broadcast :{$message}");
}
});
});
}
}
添加定时广播
function onWorkerStart(\swoole_server $server, $workerId)
{
// TODO: Implement onWorkerStart() method.
//如何避免定时器因为进程重启而丢失
//例如,我第一个进程,添加一个10秒的定时器
if($workerId == 0){
Timer::loop(3*1000,function (){
/*
* 注意 本example未引入redis来做fd信息记录,因此每次采用遍历的形式来获取结果,
* 仅供思路参考,不建议在生产环节使用
*/
$list = array();
foreach (Server::getInstance()->getServer()->connections as $fd){
$info = Server::getInstance()->getServer()->connection_info($fd);
if($info['websocket_status']){
$list[] = $fd;
}
}
//广播属于重任务,交给Task执行
AsyncTaskManager::getInstance()->add(function ()use ($list){
foreach ( $list as $fd) {
Server::getInstance()->getServer()->push($fd,"this is tick broadcast ");
}
});
});
}
}
添加监听
function beforeWorkerStart(\swoole_server $server)
{
// TODO: Implement beforeWorkerStart() method.
$server->on("message",function (\swoole_websocket_server $server, \swoole_websocket_frame $frame){
Dispatcher::getInstance(Register::class,Parser::class)->dispatchWEBSOCK($frame);
});
}
客户端代码
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div>
<div>
<p>info below</p>
<ul id="line">
</ul>
</div>
<div>
<select id="action">
<option value="who">当前fd</option>
<option value="sendTo">发给指定fd</option>
<option value="broadcast">广播</option>
</select>
<input type="text" id="says">
<button onclick="say()">发送</button>
</div>
</div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
<script>
var wsServer = 'ws://127.0.0.1:9501';
var websocket = new WebSocket(wsServer);
window.onload = function () {
websocket.onopen = function (evt) {
addLine("Connected to WebSocket server.");
};
websocket.onclose = function (evt) {
addLine("Disconnected");
};
websocket.onmessage = function (evt) {
addLine('Retrieved data from server: ' + evt.data);
};
websocket.onerror = function (evt, e) {
addLine('Error occured: ' + evt.data);
};
};
function addLine(data) {
$("#line").append("<li>"+data+"</li>");
}
function say() {
var content = $("#says").val();
var action = $("#action").val();
$("#says").val('');
websocket.send(JSON.stringify({
action:action,
content:content
}));
}
</script>
</html>
建立客户端控制器
namespace App\Controller;
use Core\AbstractInterface\AbstractController;
class Index extends AbstractController
{
function index()
{
// TODO: Implement index() method.
$this->response()->write(file_get_contents(ROOT."/App/Static/Template/client.html"));
}
function onRequest($actionName)
{
// TODO: Implement onRequest() method.
}
function actionNotFound($actionName = null, $arguments = null)
{
// TODO: Implement actionNotFound() method.
$this->response()->withStatus(404);
}
function afterAction()
{
// TODO: Implement afterAction() method.
}
}
注意,请修改配置文件,使得EasySwoole为SERVER_TYPE_WEB_SOCKET模式