自定义WEB SOCKET 命令解析

EasySwoole为了方便用户使用WEB_SOCKET进行开发,同样封装了对Swoole的sock操作。

定义命令解析

  1. namespace App\Model\WebSock;
  2. use Core\Component\Socket\AbstractInterface\AbstractClient;
  3. use Core\Component\Socket\AbstractInterface\AbstractCommandParser;
  4. use Core\Component\Socket\Common\Command;
  5. class Parser extends AbstractCommandParser
  6. {
  7. function parser(Command $result, AbstractClient $client, $rawData)
  8. {
  9. // TODO: Implement parser() method.
  10. //这里的解析规则是与客户端匹配的,等会请看客户端代码
  11. $js = json_decode($rawData,1);
  12. if(is_array($js)){
  13. if(isset($js['action'])){
  14. $result->setCommand($js['action']);
  15. }
  16. if(isset($js['content'])){
  17. $result->setMessage($js['content']);
  18. }
  19. }
  20. }
  21. }

定义命令注册类

  1. namespace App\Model\WebSock;
  2. use Core\Component\Socket\AbstractInterface\AbstractCommandRegister;
  3. use Core\Component\Socket\Client\TcpClient;
  4. use Core\Component\Socket\Common\Command;
  5. use Core\Component\Socket\Common\CommandList;
  6. use Core\Swoole\AsyncTaskManager;
  7. use Core\Swoole\Server;
  8. class Register extends AbstractCommandRegister
  9. {
  10. function register(CommandList $commandList)
  11. {
  12. // TODO: Implement register() method.
  13. $commandList->addCommandHandler('who',function (Command $command,TcpClient $client){
  14. return 'your fd is '.$client->getFd();
  15. });
  16. $commandList->addCommandHandler('sendTo',function (Command $command,TcpClient $client){
  17. $dest = intval($command->getMessage());
  18. $info = Server::getInstance()->getServer()->connection_info($dest);
  19. if($info['websocket_status']){
  20. Server::getInstance()->getServer()->push($dest,'you receive a message from '.$client->getFd());
  21. return 'send success';
  22. }else{
  23. return 'fd error';
  24. }
  25. });
  26. $commandList->addCommandHandler('broadcast',function (Command $command){
  27. /*
  28. * 注意 本example未引入redis来做fd信息记录,因此每次采用遍历的形式来获取结果,
  29. * 仅供思路参考,不建议在生产环节使用
  30. */
  31. $message = $command->getMessage();
  32. $list = array();
  33. foreach (Server::getInstance()->getServer()->connections as $fd){
  34. $info = Server::getInstance()->getServer()->connection_info($fd);
  35. if($info['websocket_status']){
  36. $list[] = $fd;
  37. }
  38. }
  39. //广播属于重任务,交给Task执行
  40. AsyncTaskManager::getInstance()->add(function ()use ($list,$message){
  41. foreach ( $list as $fd) {
  42. Server::getInstance()->getServer()->push($fd,"this is broadcast :{$message}");
  43. }
  44. });
  45. });
  46. }
  47. }

添加定时广播

  1. function onWorkerStart(\swoole_server $server, $workerId)
  2. {
  3. // TODO: Implement onWorkerStart() method.
  4. //如何避免定时器因为进程重启而丢失
  5. //例如,我第一个进程,添加一个10秒的定时器
  6. if($workerId == 0){
  7. Timer::loop(3*1000,function (){
  8. /*
  9. * 注意 本example未引入redis来做fd信息记录,因此每次采用遍历的形式来获取结果,
  10. * 仅供思路参考,不建议在生产环节使用
  11. */
  12. $list = array();
  13. foreach (Server::getInstance()->getServer()->connections as $fd){
  14. $info = Server::getInstance()->getServer()->connection_info($fd);
  15. if($info['websocket_status']){
  16. $list[] = $fd;
  17. }
  18. }
  19. //广播属于重任务,交给Task执行
  20. AsyncTaskManager::getInstance()->add(function ()use ($list){
  21. foreach ( $list as $fd) {
  22. Server::getInstance()->getServer()->push($fd,"this is tick broadcast ");
  23. }
  24. });
  25. });
  26. }
  27. }

添加监听

  1. function beforeWorkerStart(\swoole_server $server)
  2. {
  3. // TODO: Implement beforeWorkerStart() method.
  4. $server->on("message",function (\swoole_websocket_server $server, \swoole_websocket_frame $frame){
  5. Dispatcher::getInstance(Register::class,Parser::class)->dispatchWEBSOCK($frame);
  6. });
  7. }

客户端代码

  1. <html>
  2. <head>
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  4. </head>
  5. <body>
  6. <div>
  7. <div>
  8. <p>info below</p>
  9. <ul id="line">
  10. </ul>
  11. </div>
  12. <div>
  13. <select id="action">
  14. <option value="who">当前fd</option>
  15. <option value="sendTo">发给指定fd</option>
  16. <option value="broadcast">广播</option>
  17. </select>
  18. <input type="text" id="says">
  19. <button onclick="say()">发送</button>
  20. </div>
  21. </div>
  22. </body>
  23. <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.js"></script>
  24. <script>
  25. var wsServer = 'ws://127.0.0.1:9501';
  26. var websocket = new WebSocket(wsServer);
  27. window.onload = function () {
  28. websocket.onopen = function (evt) {
  29. addLine("Connected to WebSocket server.");
  30. };
  31. websocket.onclose = function (evt) {
  32. addLine("Disconnected");
  33. };
  34. websocket.onmessage = function (evt) {
  35. addLine('Retrieved data from server: ' + evt.data);
  36. };
  37. websocket.onerror = function (evt, e) {
  38. addLine('Error occured: ' + evt.data);
  39. };
  40. };
  41. function addLine(data) {
  42. $("#line").append("<li>"+data+"</li>");
  43. }
  44. function say() {
  45. var content = $("#says").val();
  46. var action = $("#action").val();
  47. $("#says").val('');
  48. websocket.send(JSON.stringify({
  49. action:action,
  50. content:content
  51. }));
  52. }
  53. </script>
  54. </html>

建立客户端控制器

  1. namespace App\Controller;
  2. use Core\AbstractInterface\AbstractController;
  3. class Index extends AbstractController
  4. {
  5. function index()
  6. {
  7. // TODO: Implement index() method.
  8. $this->response()->write(file_get_contents(ROOT."/App/Static/Template/client.html"));
  9. }
  10. function onRequest($actionName)
  11. {
  12. // TODO: Implement onRequest() method.
  13. }
  14. function actionNotFound($actionName = null, $arguments = null)
  15. {
  16. // TODO: Implement actionNotFound() method.
  17. $this->response()->withStatus(404);
  18. }
  19. function afterAction()
  20. {
  21. // TODO: Implement afterAction() method.
  22. }
  23. }

注意,请修改配置文件,使得EasySwoole为SERVER_TYPE_WEB_SOCKET模式