Подключение к websocket с PHP-клиентом

Я пытаюсь подключить клиент на основе PHP к серверу websocket.

Вот код, который я использовал, который был широко опубликован на разных форумах. Но по какой-то причине я просто не могу заставить его работать.

Любая помощь будет оценена по достоинству.

$host = 'host'; //where is the websocket server $port = 443; //ssl $local = "http://www.example.com/"; //url where this script run $data = '{"id": 2,"command": "server_info"}'; //data to be send $head = "GET / HTTP/1.1"."\r\n". "Upgrade: WebSocket"."\r\n". "Connection: Upgrade"."\r\n". "Origin: $local"."\r\n". "Host: $host"."\r\n". "Content-Length: ".strlen($data)."\r\n"."\r\n"; ////WebSocket handshake $sock = fsockopen($host, $port, $errno, $errstr, 2); fwrite($sock, $head ) or die('error:'.$errno.':'.$errstr); $headers = fread($sock, 2000); fwrite($sock, "\x00$data\xff" ) or die('error:'.$errno.':'.$errstr); $wsdata = fread($sock, 2000); //receives the data included in the websocket package "\x00DATA\xff" $retdata = trim($wsdata,"\x00\xff"); //extracts data ////WebSocket handshake fclose($sock); echo $retdata; 

Я бы предпочел использовать существующую клиентскую библиотеку websocket (возможно, https://github.com/gabrielbull/php-websocket-client или https://github.com/Devristo/phpws/tree/master/src/Devristo/Phpws / Client ?), А не сворачивать самостоятельно, но я получил его, по крайней мере, с помощью:

 $head = "GET / HTTP/1.1"."\r\n". "Host: $host"."\r\n". "Upgrade: websocket"."\r\n". "Connection: Upgrade"."\r\n". "Sec-WebSocket-Key: asdasdaas76da7sd6asd6as7d"."\r\n". "Sec-WebSocket-Version: 13"."\r\n". "Content-Length: ".strlen($data)."\r\n"."\r\n"; 

Мой сервер использует TLS / SSL, поэтому мне также необходимо:

 $sock = fsockopen('tls://'.$host, $port, $errno, $errstr, 2); 

Полная спецификация протокола: https://tools.ietf.org/rfc/rfc6455.txt

Ваш заголовок должен содержать:

 Sec-WebSocket-Key: (some value) Sec-WebSocket-Version: 13 

для успешного соединения.
Когда вы установили соединение, вам также необходимо использовать кодировку фрейма hybi10.
Смотрите: https://tools.ietf.org/rfc/rfc6455.txt – Его немного сухо.

Я сделал этот рабочий пример:

 <?php $sp=websocket_open('127.0.0.1/ws_request.php?param=php_test',$errstr); websocket_write($sp,"Websocket request message"); echo websocket_read($sp,true); $sp=websocket_open('127.0.0.1:8080/ws_request.php?param=php_test',$errstr); websocket_write($sp,"Websocket request message"); echo websocket_read($sp,true); function websocket_open($url){ $key=base64_encode(uniqid()); $query=parse_url($url); $header="GET / HTTP/1.1\r\n" ."pragma: no-cache\r\n" ."cache-control: no-cache\r\n" ."Upgrade: WebSocket\r\n" ."Connection: Upgrade\r\n" ."Sec-WebSocket-Key: $key\r\n" ."Sec-WebSocket-Version: 13\r\n" ."\r\n"; $sp=fsockopen($query['host'],$query['port'], $errno, $errstr,1); if(!$sp) die("Unable to connect to server ".$url); // Ask for connection upgrade to websocket fwrite($sp,$header); stream_set_timeout($sp,5); $reaponse_header=fread($sp, 1024); if(!strpos($reaponse_header," 101 ") || !strpos($reaponse_header,'Sec-WebSocket-Accept: ')){ die("Server did not accept to upgrade connection to websocket" .$reaponse_header); } return $sp; } function websocket_write($sp, $data,$final=true){ // Assamble header: FINal 0x80 | Opcode 0x02 $header=chr(($final?0x80:0) | 0x02); // 0x02 binary // Mask 0x80 | payload length (0-125) if(strlen($data)<126) $header.=chr(0x80 | strlen($data)); elseif (strlen($data)<0xFFFF) $header.=chr(0x80 | 126) . pack("n",strlen($data)); elseif(PHP_INT_SIZE>4) // 64 bit $header.=chr(0x80 | 127) . pack("Q",strlen($data)); else // 32 bit (pack Q dosen't work) $header.=chr(0x80 | 127) . pack("N",0) . pack("N",strlen($data)); // Add mask $mask=pack("N",rand(1,0x7FFFFFFF)); $header.=$mask; // Mask application data. for($i = 0; $i < strlen($data); $i++) $data[$i]=chr(ord($data[$i]) ^ ord($mask[$i % 4])); return fwrite($sp,$header.$data); } function websocket_read($sp,$wait_for_end=true,&$err=''){ $out_buffer=""; do{ // Read header $header=fread($sp,2); if(!$header) die("Reading header from websocket failed"); $opcode = ord($header[0]) & 0x0F; $final = ord($header[0]) & 0x80; $masked = ord($header[1]) & 0x80; $payload_len = ord($header[1]) & 0x7F; // Get payload length extensions $ext_len = 0; if($payload_len >= 0x7E){ $ext_len = 2; if($payload_len == 0x7F) $ext_len = 8; $ext=fread($sp,$ext_len); if(!$ext) die("Reading header extension from websocket failed"); // Set extented paylod length $payload_len= 0; for($i=0;$i<$ext_len;$i++) $payload_len += ord($header[$i]) << ($ext_len-$i-1)*8; } // Get Mask key if($masked){ $mask=fread($sp,4); if(!$mask) die("Reading header mask from websocket failed"); } // Get payload $frame_data=''; do{ $frame= fread($sp,$payload_len); if(!$frame) die("Reading from websocket failed."); $payload_len -= strlen($frame); $frame_data.=$frame; }while($payload_len>0); // if opcode ping, reuse headers to send a pong and continue to read if($opcode==9){ // Assamble header: FINal 0x80 | Opcode 0x02 $header[0]=chr(($final?0x80:0) | 0x0A); // 0x0A Pong fwrite($sp,$header.$ext.$mask.$frame_data); // Recieve and unmask data }elseif($opcode<3){ $data=""; if($masked) for ($i = 0; $i < $data_len; $i++) $data.= $frame_data[$i] ^ $mask[$i % 4]; else $data.= $frame_data; $out_buffer.=$data; } // wait for Final }while($wait_for_end && !$final); return $out_buffer; } в <?php $sp=websocket_open('127.0.0.1/ws_request.php?param=php_test',$errstr); websocket_write($sp,"Websocket request message"); echo websocket_read($sp,true); $sp=websocket_open('127.0.0.1:8080/ws_request.php?param=php_test',$errstr); websocket_write($sp,"Websocket request message"); echo websocket_read($sp,true); function websocket_open($url){ $key=base64_encode(uniqid()); $query=parse_url($url); $header="GET / HTTP/1.1\r\n" ."pragma: no-cache\r\n" ."cache-control: no-cache\r\n" ."Upgrade: WebSocket\r\n" ."Connection: Upgrade\r\n" ."Sec-WebSocket-Key: $key\r\n" ."Sec-WebSocket-Version: 13\r\n" ."\r\n"; $sp=fsockopen($query['host'],$query['port'], $errno, $errstr,1); if(!$sp) die("Unable to connect to server ".$url); // Ask for connection upgrade to websocket fwrite($sp,$header); stream_set_timeout($sp,5); $reaponse_header=fread($sp, 1024); if(!strpos($reaponse_header," 101 ") || !strpos($reaponse_header,'Sec-WebSocket-Accept: ')){ die("Server did not accept to upgrade connection to websocket" .$reaponse_header); } return $sp; } function websocket_write($sp, $data,$final=true){ // Assamble header: FINal 0x80 | Opcode 0x02 $header=chr(($final?0x80:0) | 0x02); // 0x02 binary // Mask 0x80 | payload length (0-125) if(strlen($data)<126) $header.=chr(0x80 | strlen($data)); elseif (strlen($data)<0xFFFF) $header.=chr(0x80 | 126) . pack("n",strlen($data)); elseif(PHP_INT_SIZE>4) // 64 bit $header.=chr(0x80 | 127) . pack("Q",strlen($data)); else // 32 bit (pack Q dosen't work) $header.=chr(0x80 | 127) . pack("N",0) . pack("N",strlen($data)); // Add mask $mask=pack("N",rand(1,0x7FFFFFFF)); $header.=$mask; // Mask application data. for($i = 0; $i < strlen($data); $i++) $data[$i]=chr(ord($data[$i]) ^ ord($mask[$i % 4])); return fwrite($sp,$header.$data); } function websocket_read($sp,$wait_for_end=true,&$err=''){ $out_buffer=""; do{ // Read header $header=fread($sp,2); if(!$header) die("Reading header from websocket failed"); $opcode = ord($header[0]) & 0x0F; $final = ord($header[0]) & 0x80; $masked = ord($header[1]) & 0x80; $payload_len = ord($header[1]) & 0x7F; // Get payload length extensions $ext_len = 0; if($payload_len >= 0x7E){ $ext_len = 2; if($payload_len == 0x7F) $ext_len = 8; $ext=fread($sp,$ext_len); if(!$ext) die("Reading header extension from websocket failed"); // Set extented paylod length $payload_len= 0; for($i=0;$i<$ext_len;$i++) $payload_len += ord($header[$i]) << ($ext_len-$i-1)*8; } // Get Mask key if($masked){ $mask=fread($sp,4); if(!$mask) die("Reading header mask from websocket failed"); } // Get payload $frame_data=''; do{ $frame= fread($sp,$payload_len); if(!$frame) die("Reading from websocket failed."); $payload_len -= strlen($frame); $frame_data.=$frame; }while($payload_len>0); // if opcode ping, reuse headers to send a pong and continue to read if($opcode==9){ // Assamble header: FINal 0x80 | Opcode 0x02 $header[0]=chr(($final?0x80:0) | 0x0A); // 0x0A Pong fwrite($sp,$header.$ext.$mask.$frame_data); // Recieve and unmask data }elseif($opcode<3){ $data=""; if($masked) for ($i = 0; $i < $data_len; $i++) $data.= $frame_data[$i] ^ $mask[$i % 4]; else $data.= $frame_data; $out_buffer.=$data; } // wait for Final }while($wait_for_end && !$final); return $out_buffer; } 

Вы можете получить полную версию здесь: https://github.com/paragi/PHP-websocket-client