代码之家  ›  专栏  ›  技术社区  ›  Oliver M Grech

PHP Websocket客户端-保持连接打开

  •  0
  • Oliver M Grech  · 技术社区  · 5 年前

    我在用 PHP-WSS 在laravel应用程序中,需要保持websocket客户端打开,才能从websocket服务器接收各种消息。

    到目前为止,我构建了一个CLI php脚本,可以执行并等待消息到达。

    我构建了以下函数来测试。。。

    问题是,为了让连接对可能从服务器发送的任何消息保持打开状态,使用while(true)循环按如下方式进行连接是一种好方法吗?我还能做得更好吗?(对我来说,它看起来很脏,希望改进它并正确地做)

    function testWebsocketClient() {
        $url = 'wss://example.com/?token=xyz123456';
        $client = new WebSocketClient($url, new ClientConfig());
        while(true){
            sleep(5);
            $client->send('test');
            $return = $client->receive(); // test received OK
        }
        return $return;
    }
    

    更新:任何使用PHP-WSS的人我都发现了一个连接错误。php中的广播方法。

    原始函数尝试在死连接上发送,显示以下错误 空读;连接断了? (注意EOF=true)

    public function broadCast(string $data): void
    {
        foreach ($this->clients as $client) {
            if (is_resource($client) ) { // check if not yet closed/broken etc
                fwrite($client, $this->encode($data));
            } else {
                echo 'Skipping a closed connection';
            }
        }
    }
    

    我把它改成了

    public function broadCast(string $data): void
    {
        foreach ($this->clients as $client) {
            //echo PHP_EOL. stream_get_status($client) .PHP_EOL;
            $clientMeta = ( stream_get_meta_data($client) );
            $clientEof = $clientMeta['eof'];
            if (is_resource($client) && $clientEof == false ) { // check if not yet closed/broken etc
                fwrite($client, $this->encode($data));
            } else {
                echo 'Skipping a closed connection';
            }
        }
    }
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Daniil Loban    5 年前

    由于呈现页面的php脚本完成了执行,因此需要使用js脚本在客户机上实现WebSocket

    <?php 
       // here output your page
       wss_path = 'wss://example.com/?token=xyz123456';
    ?>
    
    // JS script (see its working results in console by pressing `F12`)
    <script>
      let socket = new WebSocket(<?= wss_path ?>);
    
      socket.onopen = function(e) {
        console.log('[open] Connection opened');
        socket.send("Hi! I am a new web-socket client.");
      };
    
      socket.onmessage = function(event) {
        const message = JSON.parse(event.data);
        console.log('From the server:', message);
      };
    
      socket.onclose = function(event) {
        if (event.wasClean) {
          console.log(`[close] Connetion closed clearly, code=${event.code} reason=${event.reason}`);
        } else {
          console.log('[close] Сonnection closed unexpectedly');
        }
      };
    
      socket.onerror = function(error) {
        console.log(`[error] ${error}`);
      };
    </script>
    
        2
  •  0
  •   Oliver M Grech    5 年前

    经过整整一周的努力,我找到了一个解决办法。。。

    如果缓冲区为空,则有一个错误处理程序会引发异常,从而结束客户端。

    在WscMain。php文件,内置受保护的函数读取方法,删除/注释掉(或根据您的喜好和需要更改)以下错误处理程序(大约第302行)

    if(false){ // added a false condition so the code within doesn't execute
        if (false && $buff === false ) {
            $metadata = stream_get_meta_data($this->socket);
            throw new ConnectionException(
                'Broken frame, read ' . strlen($data) . ' of stated '
                . $len . ' bytes.  Stream state: '
                . json_encode($metadata),
                CommonsContract::CLIENT_BROKEN_FRAME
            );
        }
    
        if (false && $buff === '') {
            //print_r($this->socket);
            
            $metadata = stream_get_meta_data($this->socket);
            throw new ConnectionException(
                'Empty read; connection dead?  Stream state: ' . json_encode($metadata). ' '.PHP_EOL.( (int) $this->socket ) ,
                CommonsContract::CLIENT_EMPTY_READ
            );
        }
    }
    

    PS:就我而言,我还在配置中设置了一个非常小的(1秒)读取超时,如下所示。(此测试函数是从CLI PHP脚本执行的)

    function runWebsocketClientTest(){
    
        echo PHP_EOL;
        echo __FUNCTION__;
        echo PHP_EOL;
        $timeout = 1;
        $return = '';
        $config = new ClientConfig();
        $config->setTimeout($timeout);
        $client = new WebSocketClient('ws://localhost:8000', $config);
    
        while($client->isConnected()){
            if($client->isConnected()){ // this might be unnecessary 
                echo $client->receive();
            }
        }
        
    }
    

    希望这能帮助别人,感谢所有帮助过你的人。

    推荐文章