【问题标题】:PHP Websocket Client - Keep connection openPHP Websocket 客户端 - 保持连接打开
【发布时间】:2021-07-21 23:43:21
【问题描述】:

我在 laravel 应用程序中使用PHP-WSS,需要保持 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 的人我都在 BroadCast 方法中的 Connection.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';
        }
    }
}

【问题讨论】:

  • 谁在关闭你的连接?
  • Websocket 应该提供持久连接。为什么要在这里使用长轮询?
  • 如您所见,这是一个 PHP 脚本,如果我省略了无限循环,脚本将在可能的响应之前结束。不确定我是否在这里无法理解某些内容。

标签: php websocket


【解决方案1】:

由于渲染页面的php脚本执行完毕,需要在客户端自己使用js脚本实现websockets

<?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>

【讨论】:

  • 感谢您的回答,JS 解决方案是我在整个互联网上发现的,我正在尝试做同样的事情,但是使用 PHP CLI,我需要它始终作为后台运行在我的一台服务器中处理。这就是我最初的问题指定 PHP 的原因。或者也许正在考虑使用 Node.js 来使用 JS。不确定:(
  • 我明白了。因此,如果没有前端,您的解决方案与 while 更接近,但我认为您不需要在 while 正文中放入任何内容,只需一个条件即可在您想要关闭应用程序时打破循环。
  • 非常感谢您的回复。到目前为止,我在 while(true) 方面向前迈出了一大步……问题是这个 PHP-WSS 库在没有可读取的内容时抛出错误,所以我想要么我仍然缺少某些东西,要么必须写我自己的阅读流。任何进一步的提示表示赞赏,因为我觉得我的循环速度比这个上的 while(true) 循环快:S
  • 我会考虑的
  • 太好了,我很高兴能帮上忙
【解决方案2】:

经过整整一周的努力,我找到了解决方案......

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

在 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();
        }
    }
    
}

希望这对某人有所帮助,并感谢所有提供帮助的人。

【讨论】:

    猜你喜欢
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 2017-04-15
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多