WebSocketClient.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <?php
  2. namespace app\lib\wclient;
  3. use app\lib\wclient\PacketHandler as PacketHandler;
  4. class WebSocketClient
  5. {
  6. private $client;
  7. private $state;
  8. private $host;
  9. private $port;
  10. private $getParas;
  11. private $handler;
  12. private $buffer;
  13. private $openCb;
  14. private $messageCb;
  15. private $closeCb;
  16. const HANDSHAKING = 1;
  17. const HANDSHAKED = 2;
  18. const WEBSOCKET_OPCODE_CONTINUATION_FRAME = 0x0;
  19. const WEBSOCKET_OPCODE_TEXT_FRAME = 0x1;
  20. const WEBSOCKET_OPCODE_BINARY_FRAME = 0x2;
  21. const WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8;
  22. const WEBSOCKET_OPCODE_PING = 0x9;
  23. const WEBSOCKET_OPCODE_PONG = 0xa;
  24. const TOKEN_LENGHT = 16;
  25. public function __construct()
  26. {
  27. $this->client = new \swoole\client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
  28. $this->client->on("connect", [$this, "onConnect"]);
  29. $this->client->on("receive", [$this, "onReceive"]);
  30. $this->client->on("close", [$this, "onClose"]);
  31. $this->client->on("error", [$this, "onError"]);
  32. $this->handler = new PacketHandler();
  33. $this->buffer = "";
  34. }
  35. public function connect($host, $port, $getParas = '', $timeout = 0.5, $flag = 0)
  36. {
  37. $this->host = $host;
  38. $this->port = $port;
  39. $this->getParas = $getParas;
  40. $this->client->connect($host, $port, $timeout, $flag);
  41. }
  42. public function sendHandShake()
  43. {
  44. $this->state = static::HANDSHAKING;
  45. $request = $this->handler->buildHandShakeRequest($this->host, $this->port, $this->getParas);
  46. $this->client->send($request);
  47. }
  48. public function onConnect($cli)
  49. {
  50. $this->sendHandShake();
  51. }
  52. public function onReceive($cli, $data)
  53. {
  54. if ($this->state == static::HANDSHAKING) {
  55. $this->buffer .= $data;
  56. $pos = strpos($this->buffer, "\r\n\r\n", true);
  57. if ($pos != false) {
  58. $header = substr($this->buffer, 0, $pos + 4);
  59. $this->buffer = substr($this->buffer, $pos + 4);
  60. if (true == $this->handler->verifyUpgrade($header)) {
  61. $this->state = static::HANDSHAKED;
  62. if (isset($this->openCb))
  63. call_user_func($this->openCb, $this);
  64. } else {
  65. echo "handshake failed\n";
  66. }
  67. }
  68. } else if ($this->state == static::HANDSHAKED) {
  69. $this->buffer .= $data;
  70. }
  71. if ($this->state == static::HANDSHAKED) {
  72. try {
  73. $frame = $this->handler->processDataFrame($this->buffer);
  74. } catch (\Exception $e) {
  75. $cli->close();
  76. return;
  77. }
  78. if ($frame != null) {
  79. if (isset($this->messageCb))
  80. call_user_func($this->messageCb, $this, $frame);
  81. }
  82. }
  83. }
  84. public function onClose($cli)
  85. {
  86. if (isset($this->closeCb))
  87. call_user_func($this->closeCb, $this);
  88. }
  89. public function onError($cli)
  90. {
  91. echo "error occurred\n";
  92. }
  93. public function on($event, $callback)
  94. {
  95. if (strcasecmp($event, "open") === 0) {
  96. $this->openCb = $callback;
  97. } else if (strcasecmp($event, "message") === 0) {
  98. $this->messageCb = $callback;
  99. } else if (strcasecmp($event, "close") === 0) {
  100. $this->closeCb = $callback;
  101. } else {
  102. echo "$event is not supported\n";
  103. }
  104. }
  105. public function send($data, $type = 'text')
  106. {
  107. switch ($type) {
  108. case 'text':
  109. $_type = self::WEBSOCKET_OPCODE_TEXT_FRAME;
  110. break;
  111. case 'binary':
  112. case 'bin':
  113. $_type = self::WEBSOCKET_OPCODE_BINARY_FRAME;
  114. break;
  115. case 'ping':
  116. $_type = self::WEBSOCKET_OPCODE_PING;
  117. break;
  118. case 'close':
  119. $_type = self::WEBSOCKET_OPCODE_CONNECTION_CLOSE;
  120. break;
  121. case 'ping':
  122. $_type = self::WEBSOCKET_OPCODE_PING;
  123. break;
  124. case 'pong':
  125. $_type = self::WEBSOCKET_OPCODE_PONG;
  126. break;
  127. default:
  128. echo "$type is not supported\n";
  129. return;
  130. }
  131. $data = \swoole_websocket_server::pack($data, $_type);
  132. return $this->client->send($data);
  133. }
  134. public function getTcpClient()
  135. {
  136. return $this->client;
  137. }
  138. }