#php #websocket
Вопрос:
Я использую PHP-WSS в приложении laravel, и мне нужно, чтобы клиент websocket был открыт для получения различных сообщений с сервера websocket.
До сих пор я создавал php-скрипт CLI, который я могу выполнять и ждать поступления сообщений.
Я построил следующую функцию для тестирования…
Вопрос в том, чтобы сохранить соединение открытым для любых сообщений, которые могут быть отправлены с сервера, является ли хорошим подходом сделать это, как показано ниже, используя цикл 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, я обнаружил ошибку в 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) amp;amp; $clientEof == false ) { // check if not yet closed/broken etc
fwrite($client, $this->encode($data));
} else {
echo 'Skipping a closed connection';
}
}
}
Комментарии:
1. кто закрывает вашу связь?
2. Предполагается, что Websocket обеспечивает постоянное соединение. Зачем вам здесь использовать длительный опрос?
3. Как вы можете видеть, это PHP-скрипт, если я опущу бесконечный цикл, скрипт завершится до получения возможного ответа. Не уверен, что я чего-то здесь не понимаю.
Ответ №1:
Поскольку php-скрипт, отображающий страницу, завершает выполнение, вам необходимо реализовать websockets на самом клиенте с помощью js-скрипта
<?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>
Комментарии:
1. Спасибо за ваш ответ, решение JS-это то, что я находил по всему Интернету, я пытаюсь сделать то же самое, но с помощью PHP CLI мне нужно, чтобы это всегда работало в фоновом режиме на одном из моих серверов. Вот почему в моем первоначальном вопросе указан PHP. Или, может быть, возможно, рассматривал возможность использования JS с использованием узла. Не уверен 🙁
2. Я понимаю. Так что, если это без интерфейса, с которым ваше решение
while
ближе, но я думаю, что вам не нужно ничего вставлять в тело, просто одно условие, чтобы прервать цикл, когда вы хотите закрыть свое приложение.3. Мы высоко ценим ваш ответ. До сих пор я сделал хороший шаг вперед с тем временем(правда) … дело в том, что эта библиотека PHP-WSS выдает ошибку, когда читать нечего, поэтому я предполагаю, что либо мне все еще чего-то не хватает, либо мне нужно написать свой собственный поток чтения. Любые дополнительные советы приветствуются, так как я чувствую, что зацикливаюсь быстрее, чем цикл while(true) в этом :S
4. Я подумаю об этом
5. отлично, я рад, что смог помочь
Ответ №2:
После целой недели борьбы с этим я нашел решение…
Существует обработчик ошибок, который выдает исключение, если буфер пуст, тем самым завершая работу клиента.
В WscMain.php файл, внутри защищенной функции метод чтения, удалите/закомментируйте (или измените по своему вкусу и потребностям) следующий обработчик ошибок (приблизительно строка 302)
if(false){ // added a false condition so the code within doesn't execute
if (false amp;amp; $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 amp;amp; $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 сек) тайм-аут чтения в конфигурации, как показано ниже. (Эта тестовая функция выполняется из PHP-скрипта CLI)
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();
}
}
}
Надеюсь, это кому-то поможет, и спасибо всем тем, кто помогал.