Advertisement
Guest User

Untitled

a guest
Mar 29th, 2017
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.94 KB | None | 0 0
  1. <?php
  2.  
  3. $socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr);
  4.  
  5. if (!$socket) {
  6. die("$errstr ($errno)\n");
  7. }
  8.  
  9. $connects = array();
  10. while (true) {
  11. //формируем массив прослушиваемых сокетов:
  12. $read = $connects;
  13. $read []= $socket;
  14. $write = $except = null;
  15.  
  16. if (!stream_select($read, $write, $except, null)) {//ожидаем сокеты доступные для чтения (без таймаута)
  17. break;
  18. }
  19.  
  20. if (in_array($socket, $read)) {//есть новое соединение
  21. //принимаем новое соединение и производим рукопожатие:
  22. if (($connect = stream_socket_accept($socket, -1)) && $info = handshake($connect)) {
  23. $connects[] = $connect;//добавляем его в список необходимых для обработки
  24. onOpen($connect, $info);//вызываем пользовательский сценарий
  25. }
  26. unset($read[ array_search($socket, $read) ]);
  27. }
  28.  
  29. foreach($read as $connect) {//обрабатываем все соединения
  30. $data = fread($connect, 100000);
  31.  
  32. if (!$data) { //соединение было закрыто
  33. fclose($connect);
  34. unset($connects[ array_search($connect, $connects) ]);
  35. onClose($connect);//вызываем пользовательский сценарий
  36. continue;
  37. }
  38.  
  39. onMessage($connect, $data);//вызываем пользовательский сценарий
  40. }
  41. }
  42.  
  43. fclose($server);
  44.  
  45. function handshake($connect) {
  46. $info = array();
  47.  
  48. $line = fgets($connect);
  49. $header = explode(' ', $line);
  50. $info['method'] = $header[0];
  51. $info['uri'] = $header[1];
  52.  
  53. //считываем заголовки из соединения
  54. while ($line = rtrim(fgets($connect))) {
  55. if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
  56. $info[$matches[1]] = $matches[2];
  57. } else {
  58. break;
  59. }
  60. }
  61.  
  62. $address = explode(':', stream_socket_get_name($connect, true)); //получаем адрес клиента
  63. $info['ip'] = $address[0];
  64. $info['port'] = $address[1];
  65.  
  66. if (empty($info['Sec-WebSocket-Key'])) {
  67. return false;
  68. }
  69.  
  70. //отправляем заголовок согласно протоколу вебсокета
  71. $SecWebSocketAccept = base64_encode(pack('H*', sha1($info['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
  72. $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
  73. "Upgrade: websocket\r\n" .
  74. "Connection: Upgrade\r\n" .
  75. "Sec-WebSocket-Accept:$SecWebSocketAccept\r\n\r\n";
  76. fwrite($connect, $upgrade);
  77.  
  78. return $info;
  79. }
  80.  
  81. function encode($payload, $type = 'text', $masked = false)
  82. {
  83. $frameHead = array();
  84. $payloadLength = strlen($payload);
  85.  
  86. switch ($type) {
  87. case 'text':
  88. // first byte indicates FIN, Text-Frame (10000001):
  89. $frameHead[0] = 129;
  90. break;
  91.  
  92. case 'close':
  93. // first byte indicates FIN, Close Frame(10001000):
  94. $frameHead[0] = 136;
  95. break;
  96.  
  97. case 'ping':
  98. // first byte indicates FIN, Ping frame (10001001):
  99. $frameHead[0] = 137;
  100. break;
  101.  
  102. case 'pong':
  103. // first byte indicates FIN, Pong frame (10001010):
  104. $frameHead[0] = 138;
  105. break;
  106. }
  107.  
  108. // set mask and payload length (using 1, 3 or 9 bytes)
  109. if ($payloadLength > 65535) {
  110. $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
  111. $frameHead[1] = ($masked === true) ? 255 : 127;
  112. for ($i = 0; $i < 8; $i++) {
  113. $frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
  114. }
  115. // most significant bit MUST be 0
  116. if ($frameHead[2] > 127) {
  117. return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)');
  118. }
  119. } elseif ($payloadLength > 125) {
  120. $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
  121. $frameHead[1] = ($masked === true) ? 254 : 126;
  122. $frameHead[2] = bindec($payloadLengthBin[0]);
  123. $frameHead[3] = bindec($payloadLengthBin[1]);
  124. } else {
  125. $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
  126. }
  127.  
  128. // convert frame-head to string:
  129. foreach (array_keys($frameHead) as $i) {
  130. $frameHead[$i] = chr($frameHead[$i]);
  131. }
  132. if ($masked === true) {
  133. // generate a random mask:
  134. $mask = array();
  135. for ($i = 0; $i < 4; $i++) {
  136. $mask[$i] = chr(rand(0, 255));
  137. }
  138.  
  139. $frameHead = array_merge($frameHead, $mask);
  140. }
  141. $frame = implode('', $frameHead);
  142.  
  143. // append payload to frame:
  144. for ($i = 0; $i < $payloadLength; $i++) {
  145. $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
  146. }
  147.  
  148. return $frame;
  149. }
  150.  
  151. function decode($data)
  152. {
  153. $unmaskedPayload = '';
  154. $decodedData = array();
  155.  
  156. // estimate frame type:
  157. $firstByteBinary = sprintf('%08b', ord($data[0]));
  158. $secondByteBinary = sprintf('%08b', ord($data[1]));
  159. $opcode = bindec(substr($firstByteBinary, 4, 4));
  160. $isMasked = ($secondByteBinary[0] == '1') ? true : false;
  161. $payloadLength = ord($data[1]) & 127;
  162.  
  163. // unmasked frame is received:
  164. if (!$isMasked) {
  165. return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)');
  166. }
  167.  
  168. switch ($opcode) {
  169. // text frame:
  170. case 1:
  171. $decodedData['type'] = 'text';
  172. break;
  173.  
  174. case 2:
  175. $decodedData['type'] = 'binary';
  176. break;
  177.  
  178. // connection close frame:
  179. case 8:
  180. $decodedData['type'] = 'close';
  181. break;
  182.  
  183. // ping frame:
  184. case 9:
  185. $decodedData['type'] = 'ping';
  186. break;
  187.  
  188. // pong frame:
  189. case 10:
  190. $decodedData['type'] = 'pong';
  191. break;
  192.  
  193. default:
  194. return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)');
  195. }
  196.  
  197. if ($payloadLength === 126) {
  198. $mask = substr($data, 4, 4);
  199. $payloadOffset = 8;
  200. $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;
  201. } elseif ($payloadLength === 127) {
  202. $mask = substr($data, 10, 4);
  203. $payloadOffset = 14;
  204. $tmp = '';
  205. for ($i = 0; $i < 8; $i++) {
  206. $tmp .= sprintf('%08b', ord($data[$i + 2]));
  207. }
  208. $dataLength = bindec($tmp) + $payloadOffset;
  209. unset($tmp);
  210. } else {
  211. $mask = substr($data, 2, 4);
  212. $payloadOffset = 6;
  213. $dataLength = $payloadLength + $payloadOffset;
  214. }
  215.  
  216. /**
  217. * We have to check for large frames here. socket_recv cuts at 1024 bytes
  218. * so if websocket-frame is > 1024 bytes we have to wait until whole
  219. * data is transferd.
  220. */
  221. if (strlen($data) < $dataLength) {
  222. return false;
  223. }
  224.  
  225. if ($isMasked) {
  226. for ($i = $payloadOffset; $i < $dataLength; $i++) {
  227. $j = $i - $payloadOffset;
  228. if (isset($data[$i])) {
  229. $unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
  230. }
  231. }
  232. $decodedData['payload'] = $unmaskedPayload;
  233. } else {
  234. $payloadOffset = $payloadOffset - 4;
  235. $decodedData['payload'] = substr($data, $payloadOffset);
  236. }
  237.  
  238. return $decodedData;
  239. }
  240.  
  241. //пользовательские сценарии:
  242.  
  243. function onOpen($connect, $info) {
  244. var_dump($info);
  245. fwrite($connect, encode('Привет'));
  246. }
  247.  
  248. function onClose($connect) {
  249. echo "close\n";
  250. }
  251.  
  252. function onMessage($connect, $data) {
  253. echo decode($data)['payload'] . "\n";
  254. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement