Socket

安装

composer require easyswoole/socket

独立使用

此组件可脱离主框架使用,方便开发者自行创建服务进行调度,属于socket事件调度器。

  1. use EasySwoole\Socket\AbstractInterface\Controller;
  2. use EasySwoole\Socket\AbstractInterface\ParserInterface;
  3. use EasySwoole\Socket\Bean\Response;
  4. use EasySwoole\Socket\Bean\Caller;
  5. class C extends Controller{
  6. private $hit = 0;
  7. protected $hitTime = 0;
  8. function __construct()
  9. {
  10. var_dump('controller create '.spl_object_hash($this));
  11. parent::__construct();
  12. }
  13. protected function onRequest(?string $actionName): bool
  14. {
  15. $this->hit++;
  16. $this->hitTime = time();
  17. return true;
  18. }
  19. function test()
  20. {
  21. var_dump($this->hit,$this->hitTime);
  22. // co::sleep(10);
  23. $this->response()->setMessage('time:'.time());
  24. }
  25. protected function gc()
  26. {
  27. parent::gc(); // TODO: Change the autogenerated stub
  28. var_dump('controller has ben gc');
  29. }
  30. }
  31. class Parser implements ParserInterface{
  32. public function decode($raw, $client): ?Caller
  33. {
  34. // TODO: Implement decode() method.
  35. $ret = new Caller();
  36. $ret->setControllerClass(C::class);
  37. $ret->setAction('test');
  38. return $ret;
  39. }
  40. /*
  41. * 如果这里返回null,则不给客户端任何数据
  42. */
  43. public function encode(Response $response, $client): ?string
  44. {
  45. // TODO: Implement encode() method.
  46. return $response->__toString();
  47. }
  48. }
  49. $server = new \Swoole\Server("127.0.0.1", 9501);
  50. $server->set([
  51. 'worker_num'=>1
  52. ]);
  53. $conf = new \EasySwoole\Socket\Config();
  54. $conf->setType($conf::TCP);
  55. $conf->setParser(new Parser());
  56. $conf->setMaxPoolNum(2);
  57. $conf->setOnExceptionHandler(function (\swoole_server $server,\Throwable $throwable,string $raw,$client,Response $response){
  58. $response->setStatus('error');
  59. $response->setStatus($response::STATUS_RESPONSE_AND_CLOSE);
  60. });
  61. $dispatch = new \EasySwoole\Socket\Dispatcher($conf);
  62. $server->on('receive', function ($server, $fd, $reactor_id, $data)use($dispatch) {
  63. $dispatch->dispatch($server,$data,$fd,$reactor_id);
  64. });
  65. $server->on('close', function ($server, $fd) {
  66. echo "connection close: {$fd}\n";
  67. });
  68. $server->start();

框架内使用

具体demo

Tcp

控制器

  1. <?php
  2. namespace App\TcpController;
  3. use EasySwoole\Socket\AbstractInterface\Controller;
  4. class Index extends Controller
  5. {
  6. public function index()
  7. {
  8. $this->response()->setMessage('this is index');
  9. }
  10. }

解析器

  1. <?php
  2. namespace App\Parser;
  3. use EasySwoole\Socket\AbstractInterface\ParserInterface;
  4. use EasySwoole\Socket\Bean\Caller;
  5. use EasySwoole\Socket\Bean\Response;
  6. class TcpParser implements ParserInterface
  7. {
  8. public function decode($raw, $client): ?Caller
  9. {
  10. $data = substr($raw, '4');
  11. $data = json_decode($data, true);
  12. $caller = new Caller();
  13. $controller = !empty($data['controller']) ? $data['controller'] : 'Index';
  14. $action = !empty($data['action']) ? $data['action'] : 'index';
  15. $param = !empty($data['param']) ? $data['param'] : [];
  16. $controller = "App\\TcpController\\{$controller}";
  17. $caller->setControllerClass($controller);
  18. $caller->setAction($action);
  19. $caller->setArgs($param);
  20. return $caller;
  21. }
  22. public function encode(Response $response, $client): ?string
  23. {
  24. return pack('N', strlen($response->getMessage())) . $response->getMessage();
  25. }
  26. }

注册

mainServerCreate

  1. $config = new \EasySwoole\Socket\Config();
  2. $config->setType($config::TCP);
  3. $config->setParser(TcpParser::class);
  4. $dispatcher = new \EasySwoole\Socket\Dispatcher($config);
  5. $config->setOnExceptionHandler(function (\Swoole\Server $server, \Throwable $throwable, string $raw, \EasySwoole\Socket\Client\Tcp $client, \EasySwoole\Socket\Bean\Response $response) {
  6. $response->setMessage('system error!');
  7. $response->setStatus($response::STATUS_RESPONSE_AND_CLOSE);
  8. });
  9. $register->set($register::onReceive, function (\Swoole\Server $server, int $fd, int $reactorId, string $data) use ($dispatcher) {
  10. $dispatcher->dispatch($server, $data, $fd, $reactorId);
  11. });

Udp

控制器

  1. <?php
  2. namespace App\UdpController;
  3. use EasySwoole\Socket\AbstractInterface\Controller;
  4. class Index extends Controller
  5. {
  6. public function index()
  7. {
  8. $this->response()->setMessage('this is index');
  9. }
  10. }

解析器

  1. <?php
  2. namespace App\Parser;
  3. use EasySwoole\Socket\AbstractInterface\ParserInterface;
  4. use EasySwoole\Socket\Bean\Caller;
  5. use EasySwoole\Socket\Bean\Response;
  6. class UdpParser implements ParserInterface
  7. {
  8. public function decode($raw, $client): ?Caller
  9. {
  10. $data = json_decode($raw, true);
  11. $caller = new Caller();
  12. $controller = !empty($data['controller']) ? $data['controller'] : 'Index';
  13. $action = !empty($data['action']) ? $data['action'] : 'index';
  14. $param = !empty($data['param']) ? $data['param'] : [];
  15. $controller = "App\\UdpController\\{$controller}";
  16. $caller->setControllerClass($controller);
  17. $caller->setAction($action);
  18. $caller->setArgs($param);
  19. return $caller;
  20. }
  21. public function encode(Response $response, $client): ?string
  22. {
  23. return json_encode($response->getMessage());
  24. }
  25. }

注册

mainServerCreate

  1. $config = new \EasySwoole\Socket\Config();
  2. $config->setType($config::UDP);
  3. $config->setParser(UdpParser::class);
  4. $dispatcher = new \EasySwoole\Socket\Dispatcher($config);
  5. $config->setOnExceptionHandler(function (\Swoole\Server $server, \Throwable $throwable, string $raw, \EasySwoole\Socket\Client\Udp $client, \EasySwoole\Socket\Bean\Response $response) {
  6. $response->setMessage('system error!');
  7. $response->setStatus($response::STATUS_RESPONSE_AND_CLOSE);
  8. });
  9. $server = \EasySwoole\EasySwoole\ServerManager::getInstance()->getSwooleServer();
  10. $udpServer = $server->addListener('0.0.0.0', '9511', SWOOLE_UDP);
  11. $udpServer->on($register::onPacket, function (\Swoole\Server $server, string $data, array $clientInfo) use ($dispatcher) {
  12. $dispatcher->dispatch($server, $data, $clientInfo['server_socket'], $clientInfo['address'], $clientInfo['port']);
  13. });

Websocket

控制器

  1. <?php
  2. namespace App\WebSocketController;
  3. use EasySwoole\Socket\AbstractInterface\Controller;
  4. class Index extends Controller
  5. {
  6. public function index()
  7. {
  8. $this->response()->setMessage('this is index');
  9. }
  10. }

解析器

  1. <?php
  2. namespace App\Parser;
  3. use EasySwoole\Socket\AbstractInterface\ParserInterface;
  4. use EasySwoole\Socket\Bean\Caller;
  5. use EasySwoole\Socket\Bean\Response;
  6. class WebSocketParser implements ParserInterface
  7. {
  8. public function decode($raw, $client): ?Caller
  9. {
  10. $data = json_decode($raw, true);
  11. $caller = new Caller();
  12. $controller = !empty($data['controller']) ? $data['controller'] : 'Index';
  13. $action = !empty($data['action']) ? $data['action'] : 'index';
  14. $param = !empty($data['param']) ? $data['param'] : [];
  15. $controller = "App\\WebSocketController\\{$controller}";
  16. $caller->setControllerClass($controller);
  17. $caller->setAction($action);
  18. $caller->setArgs($param);
  19. return $caller;
  20. }
  21. public function encode(Response $response, $client): ?string
  22. {
  23. return json_encode($response->getMessage());
  24. }
  25. }

自定义握手

  1. <?php
  2. namespace App;
  3. class WebSocketEvent
  4. {
  5. /**
  6. * @param \Swoole\Http\Request $request
  7. * @param \Swoole\Http\Response $response
  8. * @return bool
  9. */
  10. public function onHandShake(\Swoole\Http\Request $request, \Swoole\Http\Response $response)
  11. {
  12. /** 此处自定义握手规则 返回 false 时中止握手 */
  13. if (!$this->customHandShake($request, $response)) {
  14. $response->end();
  15. return false;
  16. }
  17. /** 此处是 RFC规范中的WebSocket握手验证过程 必须执行 否则无法正确握手 */
  18. if ($this->secWebsocketAccept($request, $response)) {
  19. $response->end();
  20. return true;
  21. }
  22. $response->end();
  23. return false;
  24. }
  25. /**
  26. * @param \Swoole\Http\Request $request
  27. * @param \Swoole\Http\Response $response
  28. * @return bool
  29. */
  30. protected function customHandShake(\Swoole\Http\Request $request, \Swoole\Http\Response $response): bool
  31. {
  32. /**
  33. * 这里可以通过 http request 获取到相应的数据
  34. * 进行自定义验证后即可
  35. * (注) 浏览器中 JavaScript 并不支持自定义握手请求头 只能选择别的方式 如get参数
  36. */
  37. $headers = $request->header;
  38. $cookie = $request->cookie;
  39. // if (如果不满足我某些自定义的需求条件,返回false,握手失败) {
  40. // return false;
  41. // }
  42. return true;
  43. }
  44. /**
  45. * RFC规范中的WebSocket握手验证过程
  46. * 以下内容必须强制使用
  47. *
  48. * @param \Swoole\Http\Request $request
  49. * @param \Swoole\Http\Response $response
  50. * @return bool
  51. */
  52. protected function secWebsocketAccept(\Swoole\Http\Request $request, \Swoole\Http\Response $response): bool
  53. {
  54. // ws rfc 规范中约定的验证过程
  55. if (!isset($request->header['sec-websocket-key'])) {
  56. // 需要 Sec-WebSocket-Key 如果没有拒绝握手
  57. var_dump('shake fai1 3');
  58. return false;
  59. }
  60. if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
  61. || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
  62. ) {
  63. //不接受握手
  64. var_dump('shake fai1 4');
  65. return false;
  66. }
  67. $key = base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
  68. $headers = array(
  69. 'Upgrade' => 'websocket',
  70. 'Connection' => 'Upgrade',
  71. 'Sec-WebSocket-Accept' => $key,
  72. 'Sec-WebSocket-Version' => '13',
  73. 'KeepAlive' => 'off',
  74. );
  75. if (isset($request->header['sec-websocket-protocol'])) {
  76. $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
  77. }
  78. // 发送验证后的header
  79. foreach ($headers as $key => $val) {
  80. $response->header($key, $val);
  81. }
  82. // 接受握手 还需要101状态码以切换状态
  83. $response->status(101);
  84. var_dump('shake success at fd :' . $request->fd);
  85. return true;
  86. }
  87. }

注册

mainServerCreate

  1. <?php
  2. $config = new \EasySwoole\Socket\Config();
  3. $config->setType($config::WEB_SOCKET);
  4. $config->setParser(WebSocketParser::class);
  5. $dispatcher = new \EasySwoole\Socket\Dispatcher($config);
  6. $config->setOnExceptionHandler(function (\Swoole\Server $server, \Throwable $throwable, string $raw, \EasySwoole\Socket\Client\WebSocket $client, \EasySwoole\Socket\Bean\Response $response) {
  7. $response->setMessage('system error!');
  8. $response->setStatus($response::STATUS_RESPONSE_AND_CLOSE);
  9. });
  10. // 自定义握手
  11. /*$websocketEvent = new WebSocketEvent();
  12. $register->set(EventRegister::onHandShake, function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) use ($websocketEvent) {
  13. $websocketEvent->onHandShake($request, $response);
  14. });*/
  15. $register->set($register::onMessage, function (\Swoole\Websocket\Server $server, \Swoole\Websocket\Frame $frame) use ($dispatcher) {
  16. $dispatcher->dispatch($server, $frame->data, $frame);
  17. });