Функция pcntl_signal не срабатывает, а CTRL+C не работает при использовании сокетов

У меня есть простой PHP-скрипт, который я хочу запустить с терминала и иметь возможность обрабатывать коды сигналов. Сценарий создает TCP-сервер и обрабатывает соединения. Не знаю почему, но я не могу заставить обработку сигнала работать:

<?php
declare(ticks = 1);

// Register shutdown command.
pcntl_signal(SIGINT, function ($sig) {
  echo 'Exiting with signal: ' . $sig;
  global $sock;
  global $client;
  socket_close($sock);
  socket_close($client);
  exit(1);
});

$address = '127.0.0.1';
$port = 1234;

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sock, $address, $port) or die('Could not bind to address.');
socket_listen($sock);

while (TRUE) {
  $client = socket_accept($sock);
  if ($client) {
    $input = socket_read($client, 1024000);
    socket_write($client, var_export($input, TRUE));
    socket_write($client, 'End of data transmission.');
    socket_close($client);
  }
  usleep(100);
}

CTRL+C не уничтожает приложение и не вызывает функцию.
Если я удаляю pcntl_signal функции, CTRL+C завершает работу программы, как и ожидалось.

Основываясь на проведенном мной исследовании, эта установка должна работать. Я пробовал это в PHP 5.5 и 5.6... Не могу заставить работать как задумано.


person donutdan4114    schedule 14.11.2014    source источник


Ответы (1)


Проблема

Проблема в том, что вы используете socket_read(), который выполняет блокировку ввода-вывода. PHP не может обрабатывать сигналы, если он зависает в блокирующей операции ввода-вывода.

Решение

Используйте неблокирующий ввод-вывод для чтения данных из сокета. Вы можете использовать функцию socket_select() с тайм-аутом в цикле, чтобы убедиться, что чтение не будет заблокировано.

person hek2mgl    schedule 14.11.2014
comment
Это сработало! Я обновил свой вопрос, хотя и привел пример реального мира, который я пытаюсь настроить. Там почему-то не работает. Я предполагаю, что это может быть проблема с сокетами в целом? ... Есть ли лучший способ настроить это? - person donutdan4114; 14.11.2014
comment
Отредактировано. Я создам новый, более конкретный вопрос, спасибо! - person donutdan4114; 14.11.2014
comment
@donutdan4114 Хорошо, я думаю, это будет лучше всего. И за него проголосовали бы, так как проблема, которую вы описываете, очень интересна. Также ответ уже подготовлен для вас ;) - person hek2mgl; 14.11.2014
comment
Спасибо, я добавил все детали в вопрос, чтобы другие не путались. - person donutdan4114; 14.11.2014
comment
У вас получилось работать с socket_select()? Должен ли я привести пример? - person hek2mgl; 14.11.2014
comment
Получается, что приложение зависло на socket_accept()? $client не установлен, а другой код не срабатывает до тех пор, пока не будет установлено соединение. Это звучит правильно? - person donutdan4114; 14.11.2014
comment
Вам нужно дать ему тайм-аут, а затем запустить его в цикле - person hek2mgl; 14.11.2014
comment
Кстати, ваша проблема определенно +1 (даже если вам понадобилось две попытки;) ... Я помню, что потратил огромное время на то, чтобы выяснить это в первый раз. Это плохо документировано.. - person hek2mgl; 14.11.2014
comment
У меня это работает! Вызов socket_set_nonblock($sock); после socket_listen() работает как положено. - person donutdan4114; 14.11.2014
comment
Хорошо, это не сработало так хорошо при использовании ab -n 10000 -c 10 http://127.0.0.1:1234/... Не могли бы вы рассказать подробнее о решении тайм-аута? - person donutdan4114; 14.11.2014
comment
@ donutdan4114 donutdan4114 Может показаться, что это работает для вас, но проблема в том, что теперь вы не можете прочитать все данные с клиента. Во всех ситуациях, когда не весь вывод клиента сразу доступен, ваш скрипт ничего не читает, когда вы используете socket_set_nonblock() - person hek2mgl; 14.11.2014
comment
Давайте продолжим обсуждение в чате. - person donutdan4114; 14.11.2014