У меня есть сервер системного журнала, который слушает localhost:514
как UDP и хотел бы писать ему сообщения через этот порт. (Используя Ubuntu 14.04)
Если я запускаю любую из этих команд из bash, он печатает дату каждые 2 секунды в syslog.
# Using netcat
while true; do sleep 2; date; done | nc -u localhost 514
# Using /dev/udp
while true; do sleep 2; date; done > /dev/udp/localhost/514
Теперь просто для проверки я убиваю сервер системного журнала, а затем запускаю его через несколько секунд.
Пока процесс системного журнала мертв, /dev/udp
каждые 2 секунды выводит на консоль сообщение об ошибке, поэтому она распознает, что нет localhost:514
для этого написать.
date: write error: Connection refused
Как только системный журнал возвращается, эти сообщения об отказе в соединении прекращаются, и он возобновляет запись даты в системный журнал. Это как и ожидалось.
Но команда netcat этого не делает. Пока процесс системного журнала не работает, он не выводит никаких данных на консоль. И когда системный журнал возвращается, он не продолжает записывать дату в системный журнал.
Почему netcat не продолжает писать на localhost: 514 после перезапуска системного журнала? Как заставить netcat вести себя подобным образом /dev/udp
делает в этом примере?
Похоже, это ошибка в nc
. В nc
команда использует poll
системный вызов для ожидания получения ввода от любого stdin
или розетка.
Когда UDP-пакет был отправлен на закрытый UDP-порт на принимающей стороне, сообщение об ошибке отправляется обратно. В poll
вызов вернет этот статус в nc
команда, но nc
фактически не обрабатывает ошибку. Вместо nc
возвращается, чтобы позвонить poll
системный вызов снова, который немедленно возвращается из-за того, что ошибка все еще находится в очереди на сокете.
Это мог быть бесконечный цикл, где nc
потреблял бы все время процессора, не мог бы сделать ничего полезного.
Однако это длится только до получения следующего сообщения stdin
. С этой точки зрения poll
возвращает оба статуса в nc
, которые обрабатывают данные из stdin
. Сейчас nc
попытается записать данные из stdin
к розетке. Попытка записи в сокет доставит поставленную в очередь ошибку в nc
. Сообщение об ошибке записи вызовет nc
прекратить.
Остается сломанная труба (т.е. труба с писателем, но без ридера). Любая попытка записи в канал вызовет SIGPIPE
сигнал и если процесс не убит SIGPIPE
ошибка при вызове записи.
date
не обрабатывает SIGPIPE
, так date
убит. Итак, цикл продолжается, но каждый раз date
убивается при попытке записи в канал, где читатель давно ушел.
Что может быть сделано
Хотя nc
зацикливание на poll
Системный вызов без передачи ошибки определенно является ошибкой, исправления этой ошибки не обязательно будет достаточно для удовлетворения ваших потребностей. Просто завершается, когда ошибка возвращается из poll
можно считать правильным исправлением для nc
. Можно было представить дополнительную функцию, где nc
можно сказать, что нужно продолжать работу после ошибки, если задан конкретный флаг.
Но вы можете обойти проблему, просто перезапустив nc в таком цикле:
while sleep 1 ; do date ; done | while true ; do nc -u localhost 514 ; done
Ошибка, приводящая к периодической чрезмерной загрузке ЦП в nc
, все равно будет там. Кроме того, сочетание этой ошибки и обходного пути может привести к потере первого сообщения после повторного открытия принимающего порта.