Websocket.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. $func = $connection->onWebSocketClose;
  111. call_user_func($func, $connection);
  112. }
  113. // 默认行为是关闭连接
  114. else
  115. {
  116. $connection->close();
  117. }
  118. return 0;
  119. // ping的包
  120. case 0x9:
  121. // 如果有设置onWebSocketPing回调,尝试执行
  122. if(isset($connection->onWebSocketPing))
  123. {
  124. $func = $connection->onWebSocketPing;
  125. call_user_func($func, $connection);
  126. }
  127. // 默认发送pong
  128. else
  129. {
  130. $connection->send(pack('H*', '8a00'), true);
  131. }
  132. // 从接受缓冲区中消费掉该数据包
  133. if(!$data_len)
  134. {
  135. $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
  136. return 0;
  137. }
  138. break;
  139. // pong的包
  140. case 0xa:
  141. // 如果有设置onWebSocketPong回调,尝试执行
  142. if(isset($connection->onWebSocketPong))
  143. {
  144. $func = $connection->onWebSocketPong;
  145. call_user_func($func, $connection);
  146. }
  147. // 从接受缓冲区中消费掉该数据包
  148. if(!$data_len)
  149. {
  150. $connection->consumeRecvBuffer(self::MIN_HEAD_LEN);
  151. return 0;
  152. }
  153. break;
  154. // 错误的opcode
  155. default :
  156. $connection->close();
  157. return 0;
  158. }
  159. // websocket二进制数据
  160. $head_len = self::MIN_HEAD_LEN;
  161. if ($data_len === 126) {
  162. $pack = unpack('ntotal_len', substr($buffer, 2, 2));
  163. $data_len = $pack['total_len'];
  164. $head_len = 8;
  165. } else if ($data_len === 127) {
  166. $arr = unpack('N2', substr($buffer, 2, 8));
  167. $data_len = $arr[1]*4294967296 + $arr[2];
  168. $head_len = 14;
  169. }
  170. return $head_len + $data_len;
  171. }
  172. /**
  173. * 打包
  174. * @param string $buffer
  175. * @return string
  176. */
  177. public static function encode($buffer, ConnectionInterface $connection)
  178. {
  179. $len = strlen($buffer);
  180. $first_byte = $connection->protocolData['binaryType'];
  181. if($len<=125)
  182. {
  183. return $first_byte.chr($len).$buffer;
  184. }
  185. else if($len<=65535)
  186. {
  187. return $first_byte.chr(126).pack("n", $len).$buffer;
  188. }
  189. else
  190. {
  191. return $first_byte.chr(127).pack("xxxxN", $len).$buffer;
  192. }
  193. }
  194. /**
  195. * 解包
  196. * @param string $buffer
  197. * @return string
  198. */
  199. public static function decode($buffer, ConnectionInterface $connection)
  200. {
  201. $len = $masks = $data = $decoded = null;
  202. $len = ord($buffer[1]) & 127;
  203. if ($len === 126) {
  204. $masks = substr($buffer, 4, 4);
  205. $data = substr($buffer, 8);
  206. } else if ($len === 127) {
  207. $masks = substr($buffer, 10, 4);
  208. $data = substr($buffer, 14);
  209. } else {
  210. $masks = substr($buffer, 2, 4);
  211. $data = substr($buffer, 6);
  212. }
  213. for ($index = 0; $index < strlen($data); $index++) {
  214. $decoded .= $data[$index] ^ $masks[$index % 4];
  215. }
  216. return $decoded;
  217. }
  218. }