【问题标题】:Connecting to Websocket Via PHP - Server did not accept to upgrade connection通过 PHP 连接到 Websocket - 服务器不接受升级连接
【发布时间】:2021-10-22 22:20:08
【问题描述】:

我有以下代码,以前可以正常工作,现在突然出现错误;

我得到的错误是;

Failed to connect to server Server responed with: Server did not accept to upgrade connection to websocket.HTTP/1.1 200 OK Date: Sun, 22 Aug 2021 01:07:27 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive Last-Modified: Fri, 05 Mar 2021 07:33:32 GMT X-By: @XRPLF X-Upgrade: WebSocket X-Conn: upgrade CF-Cache-Status: DYNAMIC Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct" Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=ahbUvdpOxo1wZb%2B54qo5pEWE0KGc%2BTpWu2vgw47WhbCgjbfPwdQOGLCAZlivJyijhHs4PTt4nYVIW3ak%2BwAtlz6qhz36saYBmLZ3%2FyKJc8ZB6OJA0%2FNVp14%3D"}],"group":"cf-nel","max_age":604800} NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800} Server: cloudflare CF-RAY: 682834517edc2ce3-LHR alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400 6980

我不太确定是什么原因导致下面的代码;

<?php 
    
    include('/websocket_client.php'); 

    $server = 'xrpl.ws';
        
    $command = json_encode(array(
        'id' => 2,
        'command' => "server_info"
        
    ));
    
    
    if( $sp = websocket_open($server, 443,'',$errstr, 10, true) ) {
      websocket_write($sp,$command);
      $result = websocket_read($sp,$errstr);
    }else {
      echo "Failed to connect to server\n";
      echo "Server responed with: $errstr\n";
    }

    $result_data = json_decode($result, true);
        
    echo '<pre>';
    echo $result_data; 
    echo '</pre>';
?>

下面是Websocket_Client.php 页面太长了抱歉;但我认为包含所有内容可能很重要。

<?php
/*----------------------------------------------------------------------------*\
  Websocket client - https://github.com/paragi/PHP-websocket-client
  By Paragi 2013, Simon Riget MIT license.
  This is a demonstration of a websocket clinet.
  If you find flaws in it, please let me know at simon.riget (at) gmail
  Websockets use hybi10 frame encoding:
        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-------+-+-------------+-------------------------------+
       |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
       |I|S|S|S|  (4)  |A|     (7)     |             (16/63)           |
       |N|V|V|V|       |S|             |   (if payload len==126/127)   |
       | |1|2|3|       |K|             |                               |
       +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
       |     Extended payload length continued, if payload len == 127  |
       + - - - - - - - - - - - - - - - +-------------------------------+
       |                               |Masking-key, if MASK set to 1  |
       +-------------------------------+-------------------------------+
       | Masking-key (continued)       |          Payload Data         |
       +-------------------------------- - - - - - - - - - - - - - - - +
       :                     Payload Data continued ...                :
       + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
       |                     Payload Data continued ...                |
       +---------------------------------------------------------------+
  See: https://tools.ietf.org/rfc/rfc6455.txt
  or:  http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4.2
\*----------------------------------------------------------------------------*/

/*============================================================================*\
  Open websocket connection
  resource websocket_open(string $host [,int $port [,$additional_headers [,string &error_string ,[, int $timeout]]]]
  host
    A host URL. It can be a domain name like www.example.com or an IP address,
    with port number. Local host example: 127.0.0.1:8080
  port
  headers (optional)
    additional HTTP headers to attach to the request.
    For example to parse a session cookie: "Cookie: SID=" . session_id()
  error_string (optional)
    A referenced variable to store error messages, i any
  timeout (optional)
    The maximum time in seconds, a read operation will wait for an answer from
    the server. Default value is 10 seconds.
  ssl (optional)  
  persistant (optional)
  path (optional)
  Context (optional)
  Open a websocket connection by initiating a HTTP GET, with an upgrade request
  to websocket.
  If the server accepts, it sends a 101 response header, containing
  "Sec-WebSocket-Accept"
\*============================================================================*/
function websocket_open($host='',$port=80,$headers='',&$error_string='',$timeout=10,$ssl=false, $persistant = false, $path = '/', $context = null){

  // Generate a key (to convince server that the update is not random)
  // The key is for the server to prove it i websocket aware. (We know it is)
  $key=base64_encode(openssl_random_pseudo_bytes(16));

  $header = "GET " . $path . " HTTP/1.1\r\n"
    ."Host: $host\r\n"
    ."pragma: no-cache\r\n"
    ."Upgrade: WebSocket\r\n"
    ."Connection: Upgrade\r\n"
    ."Sec-WebSocket-Key: $key\r\n"
    ."Sec-WebSocket-Version: 13\r\n";

  // Add extra headers
  if(!empty($headers)) foreach($headers as $h) $header.=$h."\r\n";

  // Add end of header marker
  $header.="\r\n";

  // Connect to server
  $host = $host ? $host : "127.0.0.1";
  $port = $port <1 ? ( $ssl ? 443 : 80 ): $port;
  $address = ($ssl ? 'ssl://' : '') . $host . ':' . $port;
  
  $flags = STREAM_CLIENT_CONNECT | ( $persistant ? STREAM_CLIENT_PERSISTENT : 0 );
  $ctx = $context ?? stream_context_create();
  $sp = stream_socket_client($address, $errno, $errstr, $timeout, $flags, $ctx);
  
  if(!$sp){
    $error_string = "Unable to connect to websocket server: $errstr ($errno)";
    return false;
  }

  // Set timeouts
  stream_set_timeout($sp,$timeout);

  if (!$persistant or ftell($sp) === 0) {

    //Request upgrade to websocket
    $rc = fwrite($sp,$header);
    if(!$rc){
      $error_string
        = "Unable to send upgrade header to websocket server: $errstr ($errno)";
      return false;
    }

    // Read response into an assotiative array of headers. Fails if upgrade failes.
    $reaponse_header=fread($sp, 1024);

    // status code 101 indicates that the WebSocket handshake has completed.
    if (stripos($reaponse_header, ' 101 ') === false
      || stripos($reaponse_header, 'Sec-WebSocket-Accept: ') === false) {
      $error_string = "Server did not accept to upgrade connection to websocket."
        .$reaponse_header. E_USER_ERROR;
      return false;
    }
    // The key we send is returned, concatenate with "258EAFA5-E914-47DA-95CA-
    // C5AB0DC85B11" and then base64-encoded. one can verify if one feels the need...

  }
  return $sp;
}

/*============================================================================*\
  Write to websocket
  int websocket_write(resource $handle, string $data ,[boolean $final])
  Write a chunk of data through the websocket, using hybi10 frame encoding
  handle
    the resource handle returned by websocket_open, if successful
  data
    Data to transport to server
  final (optional)
    indicate if this block is the final data block of this request. Default true
  binary (optional)
    indicate if this block is sent in binary or text mode.  Default true/binary
\*============================================================================*/
function websocket_write($sp,$data,$final=true,$binary=true){
  // Assemble header: FINal 0x80 | Mode (0x02 binary, 0x01 text)

  if ($binary)
      $header=chr(($final?0x80:0) | 0x02); // 0x02 binary mode
  else
      $header=chr(($final?0x80:0) | 0x01); // 0x01 text mode

  // 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));
  else $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);
}

/*============================================================================*\
  Read from websocket
  string websocket_read(resource $handle [,string &error_string])
  read a chunk of data from the server, using hybi10 frame encoding
  handle
    the resource handle returned by websocket_open, if successful
  error_string (optional)
    A referenced variable to store error messages, i any
  Read
  Note:
    - This implementation waits for the final chunk of data, before returning.
    - Reading data while handling/ignoring other kind of packages
 \*============================================================================*/
function websocket_read($sp,&$error_string=NULL){
  $data="";

  do{
    // Read header
    $header=fread($sp,2);
    if(!$header){
      $error_string = "Reading header from websocket failed.";
      return false;
    }

    $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;
      $header=fread($sp,$ext_len);
      if(!$header){
        $error_string = "Reading header extension from websocket failed.";
        return false;
      }

      // 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){
        $error_string = "Reading header mask from websocket failed.";
        return false;
      }
    }

    // Get payload
    $frame_data='';
    while($payload_len>0){
      $frame= fread($sp,$payload_len);
      if(!$frame){
        $error_string = "Reading from websocket failed.";
        return false;
      }
      $payload_len -= strlen($frame);
      $frame_data.=$frame;
    }

    // Handle ping requests (sort of) send pong and continue to read
    if($opcode == 9){
      // Assamble header: FINal 0x80 | Opcode 0x0A + Mask on 0x80 with zero payload
      fwrite($sp,chr(0x8A) . chr(0x80) . pack("N", rand(1,0x7FFFFFFF)));
      continue;

    // Close
    } elseif($opcode == 8){
      fclose($sp);

    // 0 = continuation frame, 1 = text frame, 2 = binary frame
    }elseif($opcode < 3){
      // Unmask data
      $data_len=strlen($frame_data);
      if($masked)
        for ($i = 0; $i < $data_len; $i++)
          $data.= $frame_data[$i] ^ $mask[$i % 4];
      else
        $data.= $frame_data;

    }else
      continue;

  }while(!$final);

  return $data;
}
?>

我最初以为我已被列入黑名单或由于我发出的请求数量以及在消息中看到有关 CloudFlare 的事实,但我使用 VPN 通过主机的 IP 导航到 xrpl.ws我能够毫无问题地访问它。我也没有对 PHP ini 文件进行任何更改,所以我真的很想知道是什么原因造成的。感谢您的帮助,并对示例的长度感到抱歉。再次感谢。

我确实看到了之前的answer,其中提到了密钥的生成方式,但我研究了它,我相信它使用了一种很好的生成方法。所以我真的很茫然。

【问题讨论】:

  • 它是否适用于域xrplcluster.com
  • 我不确定是否诚实。我尝试了一些在线 websocket 测试器,它们都可以很好地连接到它。你试过不同的 PHP 客户端吗?
  • 可能 ."Origin: xrpl.ws\r\n" 现在服务器需要?
  • 只需将这一行 "Origin: https://xrpl.ws\r\n" 添加到您的 php 客户端中的 $header 中,看看会发生什么。您可能需要尝试...
  • 连接到 'wss://xrplcluster.com" 然后发送 {"command": "server_info"} 产生这个 {"result":{"info":{"build_version":"1.7. 2","complete_ledgers":"32570-65836746","hostid":"ADD","io_latency_ms":1,"jq_trans_overflow":"51255","last_close":{"converge_time_s":3.004,"proposers": 40},"load_factor":1,"peer_disconnects":"155","peer_disconnects_resources":"3","peers":d_ledger":{"age":4,"base_fee_xrp":1e-05,"hash" :"D6A2CFDAB7A8120 .cut here 因为太长了

标签: php websocket wss


【解决方案1】:

错误信息很清楚:

Server did not accept to upgrade connection to websocket.

您需要Ratchet,因为此服务器上可能没有可用的网络套接字支持。

或者它可能会发出意外的 HTTP 标头。

【讨论】:

  • 这个方法在 3 天前有效,这让我觉得它是别的东西
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-05
  • 2015-10-17
  • 1970-01-01
  • 2011-05-21
  • 2014-06-29
  • 2015-05-09
  • 2014-10-06
相关资源
最近更新 更多