рд╣рдо рд╕реНрдХреНрд░реИрдЪ рд╕реЗ PHP рдореЗрдВ рд╡реЗрдм рд╕реЙрдХреЗрдЯ рдмрдирд╛рддреЗ рд╣реИрдВ

рдХреБрдЫ рд╕рдордп рдкрд╣рд▓реЗ рдореИрдВрдиреЗ рд╡реЗрдм рд╕реЙрдХреЗрдЯреНрд╕ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдЪреБрдирд╛ред рдЗрдВрдЯрд░рдиреЗрдЯ рдкрд░, рдореБрдЭреЗ y.i рдХреЗ рд╕рд╛рде рдиреЛрдб.рдЬреЗрдПрд╕ рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рд▓реЗрдЦреЛрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкрддрд╛ рдЪрд▓рд╛, рдФрд░ рд╣рдм рдкрд░ рд╡реЗрдм рд╕реЙрдХреЗрдЯ рдкрд░ рд▓рдЧрднрдЧ рд╕рднреА рд▓реЗрдЦ phpdaemon рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рдирд┐рд░реНрджреЗрд╢ рдкрд░ рд╕реАрдорд┐рдд рдереЗред

рдореИрдВрдиреЗ phpdaemon рдФрд░ рд╢рд╛рдлрд╝реНрдЯ рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХрд╛ рдЕрдзреНрдпрдпрди рдХрд┐рдпрд╛, рд╡реЗ рдХрд╛рдлреА рд░рд╛рдХреНрд╖рд╕реА рд╣реИрдВ (рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╢рд╛рдлрд╝реНрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рд╕рдВрджреЗрд╢ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдЕрддрд┐рд░рд┐рдХреНрдд рд░реВрдк рд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреА рд╕рд┐рдлрд╛рд░рд┐рд╢ рдХреА рдЧрдИ рд╣реИ)ред рдореБрдЭреЗ рдпрд╣ рд╕рдордЭ рдореЗрдВ рдирд╣реАрдВ рдЖрдпрд╛ рдХрд┐ рдРрд╕реЗ рд░рд╛рдХреНрд╖рд╕реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреНрдпреЛрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП рдЬрд┐рдиреНрд╣реЗрдВ рдЕрдиреНрдп рд░рд╛рдХреНрд╖рд╕реЛрдВ рдХреА рд╕реНрдерд╛рдкрдирд╛ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рдЗрди рдХреЗ рд╕реНрд░реЛрдд рдХреЛрдб, рд╕рд╛рде рд╣реА рд╕рд╛рде рдЕрдиреНрдп рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЛ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж, рдореБрдЭреЗ рд▓рдЧрд╛ рдХрд┐ рд╕рдм рдХреБрдЫ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рдореИрдВ рдЕрдкрдиреЗ рдЖрдк рдореЗрдВ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡реЗрдм рд╕реЙрдХреЗрдЯ рд╕рд░реНрд╡рд░ рд▓рд┐рдЦрдирд╛ рдЪрд╛рд╣рддрд╛ рдерд╛ред рдЗрд╕рд╕реЗ рдореБрдЭреЗ рдЙрд╕ рд╕рд╛рдордЧреНрд░реА рдХреЛ рд╕рдореЗрдХрд┐рдд рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдорд┐рд▓реА, рдЬрд┐рд╕рдХрд╛ рдореИрдВрдиреЗ рдЕрдзреНрдпрдпрди рдХрд┐рдпрд╛ рдФрд░ рдХреБрдЫ рдиреБрдХрд╕рд╛рдиреЛрдВ рдкрд░ рдареЛрдХрд░ рдЦрд╛рдИ, рдЬрд┐рдирдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдореБрдЭреЗ рдХреБрдЫ рднреА рдкрддрд╛ рдирд╣реАрдВ рдерд╛ред

рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рд╕реНрдХреНрд░реИрдЪ рд╕реЗ рдореЗрд░реЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред

рдкрд░рд┐рдгрд╛рдореА рдХреЛрдб рдФрд░ рд▓реЗрдЦ рдХреЗ рдЕрдВрдд рдореЗрдВ рдбреЗрдореЛ рдЪреИрдЯ рдХреЗ рд▓рд┐рдП рдПрдХ рд▓рд┐рдВрдХред

рд▓рдХреНрд╖реНрдп:


1) php рдореЗрдВ рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯреНрд╕ рдХреЗ рд╕рд╛рде рд╕реМрджрд╛
2) рд╡реЗрдм рд╕реЙрдХреЗрдЯ рдХреЗ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХреЗ рд╕рд╛рде рд╕реМрджрд╛
3) рд╕реНрдХреНрд░реИрдЪ рд╕реЗ рд▓рд┐рдЦреЗрдВ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡реЗрдм рд╕реЙрдХреЗрдЯ рд╕рд░реНрд╡рд░

1) php рдореЗрдВ рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯ


рдЙрд╕ рдХреНрд╖рдг рддрдХ, рдореБрдЭреЗ рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯреНрд╕ рдХрд╛ рдПрдХ рдЕрд╕реНрдкрд╖реНрдЯ рд╡рд┐рдЪрд╛рд░ рдерд╛ред рд╡реЗрдм рд╕реЙрдХреЗрдЯ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХреЗ рд╕реНрд░реЛрдд рдХреЛрдб рдХреЛ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж, рдореБрдЭреЗ рдЙрдирдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд▓рд┐рдП рджреЛ рдпреЛрдЬрдирд╛рдПрдБ рдЖрдИрдВ:

"рд╕реЙрдХреЗрдЯ" рдПрдХреНрд╕рдЯреЗрдВрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);//  socket_bind($socket, '127.0.0.1', 8000);//    ip   socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);//       socket_listen($socket);//  

рдпрд╛ php рдПрдХреНрд╕рдЯреЗрдВрд╢рди "рд╕реНрдЯреНрд░реАрдо" рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░:
 $socket = stream_socket_server("tcp://127.0.0.1:8000", $errno, $errstr); 

рдореИрдВрдиреЗ рдЗрд╕рдХреА рд╕рдВрдХреНрд╖рд┐рдкреНрддрддрд╛ рдХреЗ рдХрд╛рд░рдг рджреВрд╕рд░реЗ рд╡рд┐рдХрд▓реНрдк рдХреЛ рдкреНрд░рд╛рдердорд┐рдХрддрд╛ рджреАред

рдЗрд╕рд▓рд┐рдП, рд╣рдордиреЗ рдПрдХ рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯ рдмрдирд╛рдпрд╛ рдФрд░ рдЕрдм рд╣рдо рдЗрд╕рдХреЗ рд▓рд┐рдП рдирдП рдХрдиреЗрдХреНрд╢рди рдкреНрд░реЛрд╕реЗрд╕ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЗрд╕рдХреЗ рд▓рд┐рдП рдлрд┐рд░ рд╕реЗ рджреЛ рд╡рд┐рдХрд▓реНрдк рд╣реИрдВ
 while ($connect = stream_socket_accept($socket, -1)) {//   ( ) ... $connect } 

рдПрдХ рд╕рд╛рдзрд╛рд░рдг http рд╕рд░реНрд╡рд░ рдХрд╛ рдЙрджрд╛рд╣рд░рдг рдЬреЛ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ: рдирдорд╕реНрдХрд╛рд░
 #!/usr/bin/env php <?php $socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr); if (!$socket) { die("$errstr ($errno)\n"); } while ($connect = stream_socket_accept($socket, -1)) { fwrite($connect, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n"); fclose($connect); } fclose($socket); 



рдпрд╛ stream_select рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реИ

 $connects = array(); while (true) { //   : $read = $connects; $read[] = $socket; $write = $except = null; if (!stream_select($read, $write, $except, null)) {//     ( ) break; } if (in_array($socket, $read)) {//   $connect = stream_socket_accept($socket, -1);//   $connects[] = $connect;//       unset($read[ array_search($socket, $read) ]); } foreach($read as $connect) {//   ... $connect unset($connects[ array_search($connect, $connects) ]); } } 

рдПрдХ рд╕рд░рд▓ http рд╕рд░реНрд╡рд░ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдЬрд┐рд╕рдХрд╛ рдЙрдкрдпреЛрдЧ stream_select рд╣реИ, рдЬреЛ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ: рдирдорд╕реНрдХрд╛рд░
 #!/usr/bin/env php <?php $socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr); if (!$socket) { die("$errstr ($errno)\n"); } $connects = array(); while (true) { //   : $read = $connects; $read []= $socket; $write = $except = null; if (!stream_select($read, $write, $except, null)) {//     ( ) break; } if (in_array($socket, $read)) {//   $connect = stream_socket_accept($socket, -1);//   $connects[] = $connect;//       unset($read[ array_search($socket, $read) ]); } foreach($read as $connect) {//   $headers = ''; while ($buffer = rtrim(fgets($connect))) { $headers .= $buffer; } fwrite($connect, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n"); fclose($connect); unset($connects[ array_search($connect, $connects) ]); } } fclose($server); 


рдХреНрдпреЛрдВрдХрд┐ рднрд╡рд┐рд╖реНрдп рдореЗрдВ, рд╣рдореЗрдВ рдирдП рдХрдиреЗрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рд╕рд░реНрд╡рд░ рд╕реЙрдХреЗрдЯ рдХреЛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА, рдФрд░ рдирдП рд╕рдВрджреЗрд╢реЛрдВ рдХреЗ рд▓рд┐рдП рдореМрдЬреВрджрд╛ рдХрдиреЗрдХреНрд╢рди, рдлрд┐рд░ рд╣рдо рджреВрд╕рд░реЗ рд╡рд┐рдХрд▓реНрдк рдкрд░ рд░реБрдХреЗрдВрдЧреЗред

2) рд╡реЗрдм рд╕реЙрдХреЗрдЯ рдкреНрд░реЛрдЯреЛрдХреЙрд▓


рдпрд╣ рдЖрд▓реЗрдЦ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдХрд╛ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИред
рд╣рдо рджреЛ рдмрд┐рдВрджреБрдУрдВ рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ:
"рд╣реИрдВрдбрд╢реЗрдХ" рдпрд╛ рд╣реИрдВрдбрд╢реЗрдХ:

рд╣рдо рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рд╣реЗрдбрд░ рд╕реЗ рд╕реЗрдХ-рд╡реЗрдм-рд╕реЙрдХреЗрдЯ-рдХреА рдХрд╛ рдореВрд▓реНрдп рдкрдврд╝рддреЗ рд╣реИрдВ, рдЗрд╕рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЧрдгрдирд╛ рдХрд░реЗрдВ, рд╕реЗрдХ-рд╡реЗрдм-рд╕реЙрдХреЗрдЯ-рд╕реНрд╡реАрдХрд╛рд░ рдХрд░реЗрдВ рдФрд░ рдЕрдВрддрд┐рдо рдЙрддреНрддрд░ рднреЗрдЬреЗрдВ:
 $SecWebSocketAccept = base64_encode(pack('H*', sha1($SecWebSocketKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); $response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept:$SecWebSocketAccept\r\n\r\n"; 

рдЙрджрд╛рд╣рд░рдг рдлрд╝рдВрдХреНрд╢рди рдЬреЛ рдРрд╕рд╛ рдХрд░рддрд╛ рд╣реИ
 function handshake($connect) { $info = array(); $line = fgets($connect); $header = explode(' ', $line); $info['method'] = $header[0]; $info['uri'] = $header[1]; //    while ($line = rtrim(fgets($connect))) { if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) { $info[$matches[1]] = $matches[2]; } else { break; } } $address = explode(':', stream_socket_get_name($connect, true)); //   $info['ip'] = $address[0]; $info['port'] = $address[1]; if (empty($info['Sec-WebSocket-Key'])) { return false; } //     $SecWebSocketAccept = base64_encode(pack('H*', sha1($info['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept:$SecWebSocketAccept\r\n\r\n"; fwrite($connect, $upgrade); return $info; } 



рд╕рдВрджреЗрд╢ рд╕реЗрд╡рд╛

рд╡реЗрдм рд╕реЙрдХреЗрдЯ рд╕реЗ рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдореЗрдВ рдЗрд╕реЗ рдбрд┐рдХреЛрдб рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рдФрд░ рдЗрд╕реЗ рднреЗрдЬрдиреЗ рдкрд░ рдПрдирдХреЛрдб рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред
рдПрдХ рд╣реА рд▓реЗрдЦ рдореЗрдВ рд╕рдм рдХреБрдЫ рд╕рдВрджреЗрд╢ рдХреЛрдбрд┐рдВрдЧ рдХрд╛ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣рдореЗрдВ рдХреЗрд╡рд▓ рджреЛ рдХрд╛рд░реНрдпреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ: рдбреАрдХреЛрдб рдФрд░ рдПрдирдХреЛрдбред
рдбрд┐рдХреЛрдб рдФрд░ рдПрдирдХреЛрдб рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 function decode($data) { $unmaskedPayload = ''; $decodedData = array(); // estimate frame type: $firstByteBinary = sprintf('%08b', ord($data[0])); $secondByteBinary = sprintf('%08b', ord($data[1])); $opcode = bindec(substr($firstByteBinary, 4, 4)); $isMasked = ($secondByteBinary[0] == '1') ? true : false; $payloadLength = ord($data[1]) & 127; // unmasked frame is received: if (!$isMasked) { return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)'); } switch ($opcode) { // text frame: case 1: $decodedData['type'] = 'text'; break; case 2: $decodedData['type'] = 'binary'; break; // connection close frame: case 8: $decodedData['type'] = 'close'; break; // ping frame: case 9: $decodedData['type'] = 'ping'; break; // pong frame: case 10: $decodedData['type'] = 'pong'; break; default: return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)'); } if ($payloadLength === 126) { $mask = substr($data, 4, 4); $payloadOffset = 8; $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset; } elseif ($payloadLength === 127) { $mask = substr($data, 10, 4); $payloadOffset = 14; $tmp = ''; for ($i = 0; $i < 8; $i++) { $tmp .= sprintf('%08b', ord($data[$i + 2])); } $dataLength = bindec($tmp) + $payloadOffset; unset($tmp); } else { $mask = substr($data, 2, 4); $payloadOffset = 6; $dataLength = $payloadLength + $payloadOffset; } /** * We have to check for large frames here. socket_recv cuts at 1024 bytes * so if websocket-frame is > 1024 bytes we have to wait until whole * data is transferd. */ if (strlen($data) < $dataLength) { return false; } if ($isMasked) { for ($i = $payloadOffset; $i < $dataLength; $i++) { $j = $i - $payloadOffset; if (isset($data[$i])) { $unmaskedPayload .= $data[$i] ^ $mask[$j % 4]; } } $decodedData['payload'] = $unmaskedPayload; } else { $payloadOffset = $payloadOffset - 4; $decodedData['payload'] = substr($data, $payloadOffset); } return $decodedData; } function encode($payload, $type = 'text', $masked = false) { $frameHead = array(); $payloadLength = strlen($payload); switch ($type) { case 'text': // first byte indicates FIN, Text-Frame (10000001): $frameHead[0] = 129; break; case 'close': // first byte indicates FIN, Close Frame(10001000): $frameHead[0] = 136; break; case 'ping': // first byte indicates FIN, Ping frame (10001001): $frameHead[0] = 137; break; case 'pong': // first byte indicates FIN, Pong frame (10001010): $frameHead[0] = 138; break; } // set mask and payload length (using 1, 3 or 9 bytes) if ($payloadLength > 65535) { $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 255 : 127; for ($i = 0; $i < 8; $i++) { $frameHead[$i + 2] = bindec($payloadLengthBin[$i]); } // most significant bit MUST be 0 if ($frameHead[2] > 127) { return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)'); } } elseif ($payloadLength > 125) { $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 254 : 126; $frameHead[2] = bindec($payloadLengthBin[0]); $frameHead[3] = bindec($payloadLengthBin[1]); } else { $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength; } // convert frame-head to string: foreach (array_keys($frameHead) as $i) { $frameHead[$i] = chr($frameHead[$i]); } if ($masked === true) { // generate a random mask: $mask = array(); for ($i = 0; $i < 4; $i++) { $mask[$i] = chr(rand(0, 255)); } $frameHead = array_merge($frameHead, $mask); } $frame = implode('', $frameHead); // append payload to frame: for ($i = 0; $i < $payloadLength; $i++) { $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i]; } return $frame; } 



рд╕рд░рд▓ рд╡реЗрдмрд╕реЛрдХреЗрдЯ рд╕рд░реНрд╡рд░


рдЗрд╕рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдЬрд╛рдирдХрд╛рд░реА рд╣реИред
рдкрд╣рд▓реЗ рднрд╛рдЧ рд╕реЗ рдПрдХ рд╕рд░рд▓ http рд╕рд░реНрд╡рд░ рдХреЗ рдХреЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛, рд╕рд╛рде рд╣реА рд╕рд╛рде рджреВрд╕рд░реЗ рд╕реЗ рд╣реИрдВрдбрд╢реЗрдХ, рдбреАрдХреЛрдб рдФрд░ рдПрдирдХреЛрдб рдлрд╝рдВрдХреНрд╢рди, рд╣рдо рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡реЗрдм рд╕реЙрдХреЗрдЯ рд╕рд░реНрд╡рд░ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдПрдХ рд╕рд╛рдзрд╛рд░рдг рд╡реЗрдм рд╕реЙрдХреЗрдЯ рд╕рд░реНрд╡рд░ рдХрд╛ рдЙрджрд╛рд╣рд░рдг рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
 #!/usr/bin/env php <?php $socket = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr); if (!$socket) { die("$errstr ($errno)\n"); } $connects = array(); while (true) { //   : $read = $connects; $read []= $socket; $write = $except = null; if (!stream_select($read, $write, $except, null)) {//     ( ) break; } if (in_array($socket, $read)) {//   //     : if (($connect = stream_socket_accept($socket, -1)) && $info = handshake($connect)) { $connects[] = $connect;//       onOpen($connect, $info);//   } unset($read[ array_search($socket, $read) ]); } foreach($read as $connect) {//   $data = fread($connect, 100000); if (!$data) { //   fclose($connect); unset($connects[ array_search($connect, $connects) ]); onClose($connect);//   continue; } onMessage($connect, $data);//   } } fclose($server); function handshake($connect) { $info = array(); $line = fgets($connect); $header = explode(' ', $line); $info['method'] = $header[0]; $info['uri'] = $header[1]; //    while ($line = rtrim(fgets($connect))) { if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) { $info[$matches[1]] = $matches[2]; } else { break; } } $address = explode(':', stream_socket_get_name($connect, true)); //   $info['ip'] = $address[0]; $info['port'] = $address[1]; if (empty($info['Sec-WebSocket-Key'])) { return false; } //     $SecWebSocketAccept = base64_encode(pack('H*', sha1($info['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept:$SecWebSocketAccept\r\n\r\n"; fwrite($connect, $upgrade); return $info; } function encode($payload, $type = 'text', $masked = false) { $frameHead = array(); $payloadLength = strlen($payload); switch ($type) { case 'text': // first byte indicates FIN, Text-Frame (10000001): $frameHead[0] = 129; break; case 'close': // first byte indicates FIN, Close Frame(10001000): $frameHead[0] = 136; break; case 'ping': // first byte indicates FIN, Ping frame (10001001): $frameHead[0] = 137; break; case 'pong': // first byte indicates FIN, Pong frame (10001010): $frameHead[0] = 138; break; } // set mask and payload length (using 1, 3 or 9 bytes) if ($payloadLength > 65535) { $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 255 : 127; for ($i = 0; $i < 8; $i++) { $frameHead[$i + 2] = bindec($payloadLengthBin[$i]); } // most significant bit MUST be 0 if ($frameHead[2] > 127) { return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)'); } } elseif ($payloadLength > 125) { $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 254 : 126; $frameHead[2] = bindec($payloadLengthBin[0]); $frameHead[3] = bindec($payloadLengthBin[1]); } else { $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength; } // convert frame-head to string: foreach (array_keys($frameHead) as $i) { $frameHead[$i] = chr($frameHead[$i]); } if ($masked === true) { // generate a random mask: $mask = array(); for ($i = 0; $i < 4; $i++) { $mask[$i] = chr(rand(0, 255)); } $frameHead = array_merge($frameHead, $mask); } $frame = implode('', $frameHead); // append payload to frame: for ($i = 0; $i < $payloadLength; $i++) { $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i]; } return $frame; } function decode($data) { $unmaskedPayload = ''; $decodedData = array(); // estimate frame type: $firstByteBinary = sprintf('%08b', ord($data[0])); $secondByteBinary = sprintf('%08b', ord($data[1])); $opcode = bindec(substr($firstByteBinary, 4, 4)); $isMasked = ($secondByteBinary[0] == '1') ? true : false; $payloadLength = ord($data[1]) & 127; // unmasked frame is received: if (!$isMasked) { return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)'); } switch ($opcode) { // text frame: case 1: $decodedData['type'] = 'text'; break; case 2: $decodedData['type'] = 'binary'; break; // connection close frame: case 8: $decodedData['type'] = 'close'; break; // ping frame: case 9: $decodedData['type'] = 'ping'; break; // pong frame: case 10: $decodedData['type'] = 'pong'; break; default: return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)'); } if ($payloadLength === 126) { $mask = substr($data, 4, 4); $payloadOffset = 8; $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset; } elseif ($payloadLength === 127) { $mask = substr($data, 10, 4); $payloadOffset = 14; $tmp = ''; for ($i = 0; $i < 8; $i++) { $tmp .= sprintf('%08b', ord($data[$i + 2])); } $dataLength = bindec($tmp) + $payloadOffset; unset($tmp); } else { $mask = substr($data, 2, 4); $payloadOffset = 6; $dataLength = $payloadLength + $payloadOffset; } /** * We have to check for large frames here. socket_recv cuts at 1024 bytes * so if websocket-frame is > 1024 bytes we have to wait until whole * data is transferd. */ if (strlen($data) < $dataLength) { return false; } if ($isMasked) { for ($i = $payloadOffset; $i < $dataLength; $i++) { $j = $i - $payloadOffset; if (isset($data[$i])) { $unmaskedPayload .= $data[$i] ^ $mask[$j % 4]; } } $decodedData['payload'] = $unmaskedPayload; } else { $payloadOffset = $payloadOffset - 4; $decodedData['payload'] = substr($data, $payloadOffset); } return $decodedData; } // : function onOpen($connect, $info) { echo "open\n"; fwrite($connect, encode('')); } function onClose($connect) { echo "close\n"; } function onMessage($connect, $data) { echo decode($data)['payload'] . "\n"; } 



рдЙрдкрд░реЛрдХреНрдд рдЙрджрд╛рд╣рд░рдг рдореЗрдВ, рдЖрдк рдЖрд╡рд╢реНрдпрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП onOpen, onClose рдФрд░ onMessage рдкрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдмрджрд▓ рд╕рдХрддреЗ рд╣реИрдВред

рд▓рдХреНрд╖реНрдп рдкреНрд░рд╛рдкреНрдд рд╣реЛрддреЗ рд╣реИрдВред
рдпрджрд┐ рдпрд╣ рд╕рд╛рдордЧреНрд░реА рдЖрдкрдХреЛ рджрд┐рд▓рдЪрд╕реНрдк рд▓рдЧрддреА рд╣реИ, рддреЛ рдЕрдЧрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рд╡рд░реНрдгрди рдХрд░реВрдВрдЧрд╛ рдХрд┐ рдЖрдк рдХреИрд╕реЗ рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХрдиреЗрдХреНрд╢рди (рдПрдХ рдорд╛рд╕реНрдЯрд░ рдФрд░ рдХрдИ рд╢реНрд░рдорд┐рдХреЛрдВ) рдХреЗ рд▓рд┐рдП рдХрдИ рдкреНрд░рдХреНрд░рд┐рдпрд╛рдПрдВ рдЪрд▓рд╛ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрдВрдЯрд░рдкреНрд░реЛрд╕реЗрд╕ рд╕рдВрдЪрд╛рд░, yii рдШрдЯрдХ рдХреЗ рдЙрджрд╛рд╣рд░рдг рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЕрдкрдиреЗ рдврд╛рдВрдЪреЗ рдХреЗ рд╕рд╛рде рдПрдХреАрдХрд░рдгред

рдЙрдкрд░реЛрдХреНрдд рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреЗ рд╕рд╛рде рдбреЗрдореЛ рдЪреИрдЯ

рдбреЗрдореЛ рдЪреИрдЯ рдХреЛрдб
 #!/usr/bin/env php <?php class WebsocketServer { public function __construct($config) { $this->config = $config; } public function start() { //   $server = stream_socket_server("tcp://{$this->config['host']}:{$this->config['port']}", $errorNumber, $errorString); if (!$server) { die("error: stream_socket_server: $errorString ($errorNumber)\r\n"); } list($pid, $master, $workers) = $this->spawnWorkers();//   if ($pid) {// fclose($server);//         $WebsocketMaster = new WebsocketMaster($workers);//      $WebsocketMaster->start(); } else {// $WebsocketHandler = new WebsocketHandler($server, $master); $WebsocketHandler->start(); } } protected function spawnWorkers() { $master = null; $workers = array(); $i = 0; while ($i < $this->config['workers']) { $i++; //  ,        $pair = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); $pid = pcntl_fork();//  if ($pid == -1) { die("error: pcntl_fork\r\n"); } elseif ($pid) { // fclose($pair[0]); $workers[$pid] = $pair[1];//      } else { // fclose($pair[1]); $master = $pair[0];//   break; } } return array($pid, $master, $workers); } } class WebsocketMaster { protected $workers = array(); protected $clients = array(); public function __construct($workers) { $this->clients = $this->workers = $workers; } public function start() { while (true) { //   ,    $read = $this->clients; stream_select($read, $write, $except, null);//  ,    if ($read) {//     foreach ($read as $client) { $data = fread($client, 1000); if (!$data) { //   unset($this->clients[intval($client)]); @fclose($client); continue; } foreach ($this->workers as $worker) {//     if ($worker !== $client) { fwrite($worker, $data); } } } } } } } abstract class WebsocketWorker { protected $clients = array(); protected $server; protected $master; protected $pid; protected $handshakes = array(); protected $ips = array(); public function __construct($server, $master) { $this->server = $server; $this->master = $master; $this->pid = posix_getpid(); } public function start() { while (true) { //   ,    $read = $this->clients; $read[] = $this->server; $read[] = $this->master; $write = array(); if ($this->handshakes) { foreach ($this->handshakes as $clientId => $clientInfo) { if ($clientInfo) { $write[] = $this->clients[$clientId]; } } } stream_select($read, $write, $except, null);//  ,    if (in_array($this->server, $read)) { //        //     ,    if ($client = stream_socket_accept($this->server, -1)) { $address = explode(':', stream_socket_get_name($client, true)); if (isset($this->ips[$address[0]]) && $this->ips[$address[0]] > 5) {//      ip @fclose($client); } else { @$this->ips[$address[0]]++; $this->clients[intval($client)] = $client; $this->handshakes[intval($client)] = array();//,     } } //    ,          unset($read[array_search($this->server, $read)]); } if (in_array($this->master, $read)) { //    $data = fread($this->master, 1000); $this->onSend($data);//   //   ,          unset($read[array_search($this->master, $read)]); } if ($read) {//     foreach ($read as $client) { if (isset($this->handshakes[intval($client)])) { if ($this->handshakes[intval($client)]) {//       continue;//            } if (!$this->handshake($client)) { unset($this->clients[intval($client)]); unset($this->handshakes[intval($client)]); $address = explode(':', stream_socket_get_name($client, true)); if (isset($this->ips[$address[0]]) && $this->ips[$address[0]] > 0) { @$this->ips[$address[0]]--; } @fclose($client); } } else { $data = fread($client, 1000); if (!$data) { //   unset($this->clients[intval($client)]); unset($this->handshakes[intval($client)]); $address = explode(':', stream_socket_get_name($client, true)); if (isset($this->ips[$address[0]]) && $this->ips[$address[0]] > 0) { @$this->ips[$address[0]]--; } @fclose($client); $this->onClose($client);//   continue; } $this->onMessage($client, $data);//   } } } if ($write) { foreach ($write as $client) { if (!$this->handshakes[intval($client)]) {//        continue;//      } $info = $this->handshake($client); $this->onOpen($client, $info);//   } } } } protected function handshake($client) { $key = $this->handshakes[intval($client)]; if (!$key) { //    $headers = fread($client, 10000); preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match); if (empty($match[1])) { return false; } $key = $match[1]; $this->handshakes[intval($client)] = $key; } else { //     $SecWebSocketAccept = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept:$SecWebSocketAccept\r\n\r\n"; fwrite($client, $upgrade); unset($this->handshakes[intval($client)]); } return $key; } protected function encode($payload, $type = 'text', $masked = false) { $frameHead = array(); $payloadLength = strlen($payload); switch ($type) { case 'text': // first byte indicates FIN, Text-Frame (10000001): $frameHead[0] = 129; break; case 'close': // first byte indicates FIN, Close Frame(10001000): $frameHead[0] = 136; break; case 'ping': // first byte indicates FIN, Ping frame (10001001): $frameHead[0] = 137; break; case 'pong': // first byte indicates FIN, Pong frame (10001010): $frameHead[0] = 138; break; } // set mask and payload length (using 1, 3 or 9 bytes) if ($payloadLength > 65535) { $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 255 : 127; for ($i = 0; $i < 8; $i++) { $frameHead[$i + 2] = bindec($payloadLengthBin[$i]); } // most significant bit MUST be 0 if ($frameHead[2] > 127) { return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)'); } } elseif ($payloadLength > 125) { $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8); $frameHead[1] = ($masked === true) ? 254 : 126; $frameHead[2] = bindec($payloadLengthBin[0]); $frameHead[3] = bindec($payloadLengthBin[1]); } else { $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength; } // convert frame-head to string: foreach (array_keys($frameHead) as $i) { $frameHead[$i] = chr($frameHead[$i]); } if ($masked === true) { // generate a random mask: $mask = array(); for ($i = 0; $i < 4; $i++) { $mask[$i] = chr(rand(0, 255)); } $frameHead = array_merge($frameHead, $mask); } $frame = implode('', $frameHead); // append payload to frame: for ($i = 0; $i < $payloadLength; $i++) { $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i]; } return $frame; } protected function decode($data) { $unmaskedPayload = ''; $decodedData = array(); // estimate frame type: $firstByteBinary = sprintf('%08b', ord($data[0])); $secondByteBinary = sprintf('%08b', ord($data[1])); $opcode = bindec(substr($firstByteBinary, 4, 4)); $isMasked = ($secondByteBinary[0] == '1') ? true : false; $payloadLength = ord($data[1]) & 127; // unmasked frame is received: if (!$isMasked) { return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)'); } switch ($opcode) { // text frame: case 1: $decodedData['type'] = 'text'; break; case 2: $decodedData['type'] = 'binary'; break; // connection close frame: case 8: $decodedData['type'] = 'close'; break; // ping frame: case 9: $decodedData['type'] = 'ping'; break; // pong frame: case 10: $decodedData['type'] = 'pong'; break; default: return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)'); } if ($payloadLength === 126) { $mask = substr($data, 4, 4); $payloadOffset = 8; $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset; } elseif ($payloadLength === 127) { $mask = substr($data, 10, 4); $payloadOffset = 14; $tmp = ''; for ($i = 0; $i < 8; $i++) { $tmp .= sprintf('%08b', ord($data[$i + 2])); } $dataLength = bindec($tmp) + $payloadOffset; unset($tmp); } else { $mask = substr($data, 2, 4); $payloadOffset = 6; $dataLength = $payloadLength + $payloadOffset; } /** * We have to check for large frames here. socket_recv cuts at 1024 bytes * so if websocket-frame is > 1024 bytes we have to wait until whole * data is transferd. */ if (strlen($data) < $dataLength) { return false; } if ($isMasked) { for ($i = $payloadOffset; $i < $dataLength; $i++) { $j = $i - $payloadOffset; if (isset($data[$i])) { $unmaskedPayload .= $data[$i] ^ $mask[$j % 4]; } } $decodedData['payload'] = $unmaskedPayload; } else { $payloadOffset = $payloadOffset - 4; $decodedData['payload'] = substr($data, $payloadOffset); } return $decodedData; } abstract protected function onOpen($client, $info); abstract protected function onClose($client); abstract protected function onMessage($client, $data); abstract protected function onSend($data); abstract protected function send($data); } //   class WebsocketHandler extends WebsocketWorker { protected function onOpen($client, $info) {//      } protected function onClose($client) {//     } protected function onMessage($client, $data) {//      $data = $this->decode($data); if (!$data['payload']) { return; } if (!mb_check_encoding($data['payload'], 'utf-8')) { return; } //var_export($data); //  ,  ,      $message = ' #' . intval($client) . ' (' . $this->pid . '): ' . strip_tags($data['payload']); $this->send($message); $this->sendHelper($message); } protected function onSend($data) {//      $this->sendHelper($data); } protected function send($message) {//   ,        @fwrite($this->master, $message); } private function sendHelper($data) { $data = $this->encode($data); $write = $this->clients; if (stream_select($read, $write, $except, 0)) { foreach ($write as $client) { @fwrite($client, $data); } } } } $config = array( 'host' => '0.0.0.0', 'port' => 8000, 'workers' => 1, ); $WebsocketServer = new WebsocketServer($config); $WebsocketServer->start(); 


рдЕрдкрдбреЗрдЯ (рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдХрд╛ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛):



рд▓реЗрдЦ рдХрд╛ рджреВрд╕рд░рд╛ рднрд╛рдЧ: рдЖрдИ.рдкреА.рд╕реА. рдЗрдВрдЯрд░рдкреНрд░реЛрд╕реЗрд╕ рд╕рдВрдЪрд╛рд░
рднрд╛рдЧ рддреАрди: рдЪреИрдЯ рд╕реЗ рдЧреЗрдо рддрдХ: рдмреИрдЯрд▓ рд╕рд┐рдЯреА
рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХрд╛ рд╕реНрд░реЛрдд рдХреЛрдб рдФрд░ рдЙрджрд╛рд╣рд░рдг рдЬреАрдердм рдкрд░ рд╣реИ рдФрд░ рдПрдордЖрдИрдЯреА рд▓рд╛рдЗрд╕реЗрдВрд╕ рдХреЗ рддрд╣рдд рдЙрдкрд▓рдмреНрдз рд╣реИ

Source: https://habr.com/ru/post/In209864/


All Articles