Websocket.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. namespace Workerman\Protocols;
  3. /**
  4. * WebSocket 协议服务端解包和打包
  5. * @author walkor <walkor@workerman.net>
  6. */
  7. use Workerman\Connection\ConnectionInterface;
  8. class Websocket implements \Workerman\Protocols\ProtocolInterface
  9. {
  10. /**
  11. * websocket头部最小长度
  12. * @var int
  13. */
  14. const MIN_HEAD_LEN = 6;
  15. /**
  16. * websocket blob类型
  17. * @var char
  18. */
  19. const BINARY_TYPE_BLOB = 0x81;
  20. /**
  21. * websocket arraybuffer类型
  22. * @var char
  23. */
  24. const BINARY_TYPE_ARRAYBUFFER = 0x82;
  25. /**
  26. * 检查包的完整性
  27. * @param string $buffer
  28. */
  29. public static function input($buffer, ConnectionInterface $connection)
  30. {
  31. // 数据长度
  32. $recv_len = strlen($buffer);
  33. // 长度不够
  34. if($recv_len < self::MIN_HEAD_LEN)
  35. {
  36. return 0;
  37. }
  38. // 还没有握手
  39. if(empty($connection->handshake))
  40. {
  41. // 握手阶段客户端发送HTTP协议
  42. if(0 === strpos($buffer, 'GET'))
  43. {
  44. // 判断\r\n\r\n边界
  45. $heder_end_pos = strpos($buffer, "\r\n\r\n");
  46. if(!$heder_end_pos)
  47. {
  48. return 0;
  49. }
  50. // 解析Sec-WebSocket-Key
  51. $Sec_WebSocket_Key = '';
  52. if(preg_match("/Sec-WebSocket-Key: *(.*?)\r\n/", $buffer, $match))
  53. {
  54. $Sec_WebSocket_Key = $match[1];
  55. }
  56. $new_key = base64_encode(sha1($Sec_WebSocket_Key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
  57. // 握手返回的数据
  58. $new_message = "HTTP/1.1 101 Switching Protocols\r\n";
  59. $new_message .= "Upgrade: websocket\r\n";
  60. $new_message .= "Sec-WebSocket-Version: 13\r\n";
  61. $new_message .= "Connection: Upgrade\r\n";
  62. $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
  63. $connection->handshake = true;
  64. $connection->consumeRecvBuffer(strlen($buffer));
  65. $connection->send($new_message, true);
  66. $connection->protocolData = array(
  67. 'binaryType' => self::BINARY_TYPE_BLOB, // blob or arraybuffer
  68. );
  69. // 如果有设置onWebSocketConnect回调,尝试执行
  70. if(isset($connection->onWebSocketConnect))
  71. {
  72. call_user_func(array($connection, 'onWebSocketConnect'), $connection);
  73. }
  74. return 0;
  75. }
  76. // 如果是flash的policy-file-request
  77. elseif(0 === strpos($buffer,'<polic'))
  78. {
  79. if('>' != $buffer[strlen($buffer) - 1])
  80. {
  81. return 0;
  82. }
  83. $policy_xml = '<?xml version="1.0"?><cross-domain-policy><site-control permitted-cross-domain-policies="all"/><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'."\0";
  84. $connection->send($policy_xml, true);
  85. $connection->consumeRecvBuffer(strlen($buffer));
  86. return 0;
  87. }
  88. // 出错
  89. $connection->close();
  90. return 0;
  91. }
  92. $data_len = ord($buffer[1]) & 127;
  93. $opcode = ord($buffer[0]) & 0xf;
  94. switch($opcode)
  95. {
  96. // 附加数据帧 @todo 实现附加数据帧
  97. case 0x0:
  98. break;
  99. // 文本数据帧
  100. case 0x1:
  101. break;
  102. // 二进制数据帧
  103. case 0x2:
  104. break;
  105. // 关闭的包
  106. case 0x8:
  107. // 如果有设置onWebSocketClose回调,尝试执行
  108. if(isset($connection->onWebSocketClose))
  109. {
  110. call_user_func(array($connection, 'onWebSocketClose'), $connection);
  111. }
  112. // 默认行为是关闭连接
  113. else
  114. {
  115. $connection->close();
  116. }
  117. return 0;
  118. // ping的包
  119. case 0x9:
  120. // 如果有设置onWebSocketPing回调,尝试执行
  121. if(isset($connection->onWebSocketPing))
  122. {
  123. call_user_func(array($connection, 'onWebSocketPing'), $connection);
  124. }
  125. // 默认发送pong
  126. else
  127. {
  128. $connection->send(pack('H*', '8a00'));
  129. }
  130. // 从接受缓冲区中消费掉该数据包
  131. if(!$data_len)
  132. {
  133. $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
  134. return 0;
  135. }
  136. break;
  137. // pong的包
  138. case 0xa:
  139. // 如果有设置onWebSocketPong回调,尝试执行
  140. if(isset($connection->onWebSocketPong))
  141. {
  142. call_user_func(array($connection, 'onWebSocketPong'), $connection);
  143. }
  144. // 从接受缓冲区中消费掉该数据包
  145. if(!$data_len)
  146. {
  147. $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
  148. return 0;
  149. }
  150. break;
  151. // 错误的opcode
  152. default :
  153. $connection->close();
  154. return 0;
  155. }
  156. // websocket二进制数据
  157. $head_len = self::MIN_HEAD_LEN;
  158. if ($data_len === 126) {
  159. $pack = unpack('ntotal_len', substr($buffer, 2, 2));
  160. $data_len = $pack['total_len'];
  161. $head_len = 8;
  162. } else if ($data_len === 127) {
  163. $arr = unpack('N2', substr($buffer, 2, 8));
  164. $data_len = $arr[1]*4294967296 + $arr[2];
  165. $head_len = 14;
  166. }
  167. return $head_len + $data_len;
  168. }
  169. /**
  170. * 打包
  171. * @param string $buffer
  172. * @return string
  173. */
  174. public static function encode($buffer, ConnectionInterface $connection)
  175. {
  176. $len = strlen($buffer);
  177. $first_byte = $connection->protocolData['binaryType'];
  178. if($len<=125)
  179. {
  180. return $first_byte.chr($len).$buffer;
  181. }
  182. else if($len<=65535)
  183. {
  184. return $first_byte.chr(126).pack("n", $len).$buffer;
  185. }
  186. else
  187. {
  188. return $first_byte.chr(127).pack("xxxxN", $len).$buffer;
  189. }
  190. }
  191. /**
  192. * 解包
  193. * @param string $buffer
  194. * @return string
  195. */
  196. public static function decode($buffer, ConnectionInterface $connection)
  197. {
  198. $len = $masks = $data = $decoded = null;
  199. $len = ord($buffer[1]) & 127;
  200. if ($len === 126) {
  201. $masks = substr($buffer, 4, 4);
  202. $data = substr($buffer, 8);
  203. } else if ($len === 127) {
  204. $masks = substr($buffer, 10, 4);
  205. $data = substr($buffer, 14);
  206. } else {
  207. $masks = substr($buffer, 2, 4);
  208. $data = substr($buffer, 6);
  209. }
  210. for ($index = 0; $index < strlen($data); $index++) {
  211. $decoded .= $data[$index] ^ $masks[$index % 4];
  212. }
  213. return $decoded;
  214. }
  215. }