Назад | Перейти на главную страницу

Разница между / dev / udp и netcat

У меня есть сервер системного журнала, который слушает 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, все равно будет там. Кроме того, сочетание этой ошибки и обходного пути может привести к потере первого сообщения после повторного открытия принимающего порта.