/dev/udp 和 netcat 的区别

use*_*159 5 networking syslog udp netcat rsyslog

我有一个 syslog 服务器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
Run Code Online (Sandbox Code Playgroud)

现在只是为了测试一些东西,我杀死了系统日志服务器,然后在几秒钟后启动它。

当 syslog 进程停止时,该/dev/udp命令每 2 秒向控制台打印一条错误消息,因此它识别出没有localhost:514可写入的内容。

date: write error: Connection refused
Run Code Online (Sandbox Code Playgroud)

一旦 syslog 返回,这些连接被拒绝的消息就会停止,并继续将日期写入 syslog。这正如预期的那样。

但是 netcat 命令不会这样做。当 syslog 进程停止时,它不会将任何输出打印到控制台。当 syslog 返回时,它不会继续将日期写入 syslog。

为什么重新启动 syslog 时 netcat 不会继续写入 localhost:514?我怎样才能让 netcat 像/dev/udp这个例子中那样表现?

kas*_*erd 5

这似乎是nc. 该nc命令使用poll系统调用来等待,直到从stdin套接字或套接字接收到输入。

当 UDP 数据包已发送到接收端关闭的 UDP 端口时,会发回错误消息。的poll通话将这个状态返回nc指令,但nc并不实际处理错误。而是nc返回再次调用poll系统调用,由于错误仍在套接字上排队,系统调用立即返回。

这可能是一个无限循环,在那里nc会消耗所有的 CPU 时间,它可以做任何有用的事情。

然而,它只持续到在 上接收到下一条消息stdin。此时poll将两个状态返回到nc,它处理来自 的数据stdin。现在nc将尝试将数据写入stdin套接字。尝试写入套接字会将排队的错误传递给nc。写入错误消息将导致nc终止。

这给您留下了一个破损的管道(即一个有写入器但没有读取器的管道)。任何写入管道的尝试都会触发一个SIGPIPE信号,如果进程没有被SIGPIPE写入调用的错误杀死。

date没有处理SIGPIPE,所以date被杀了。所以循环继续进行,但每次date尝试写入读取器早已消失的管道时都会被杀死。

可以做什么

尽管ncpoll不处理错误的情况下循环系统调用绝对是一个错误,但修复该错误不一定足以满足您的需求。一旦错误返回,简单地终止poll可能被认为是nc. 人们可以想象一个额外的功能,nc如果给定一个特定的标志,可以告诉它在发生错误后继续前进。

但是您可以通过在这样的循环中简单地重新启动 nc 来解决这个问题:

while sleep 1 ; do date ; done | while true ; do nc -u localhost 514 ; done
Run Code Online (Sandbox Code Playgroud)

中导致周期性 CPU 使用率过高的错误nc仍然存在。此外,该错误和解决方法的结合可能会导致重新打开接收端口后的第一条消息丢失。