UDP 上的 netcat 问题

UDP 上的 netcat 问题

在解决所述问题时这里,我尝试了一些非常基本的东西:我尝试通过 UDP 建立一个非常基本的 netcat 连接,但我注意到它没有像我预期的那样工作。

与下面的 TCP 图示一样,我期望 UDP 案例在窗口 1 上回显与我在窗口 2 中键入的相同的输出;相反,在 UDP 案例中,我在窗口 1 中没有得到任何输出。

我在笔记本电脑上运行 Debian Jessie,并且使用以下捕获过滤器运行 Wireshark:

udp and not (port 123 or port 5353 or port 1900)

在一个终端窗口中,我通过运行以下命令开始 TCP 测试:

$ nc -l 6900

在第二个终端窗口中,我运行了以下命令:

$ nc localhost 6900

在窗口 2 中,我输入“one”并按 Enter,然后输入“two”并按 Enter,最后按 Ctrl+D 退出。

在“one”上按下 Enter 键之后,我看到“one”在窗口 1 中回显。在“two”上按下 Enter 键之后,我看到“two”在窗口 1 中回显。当我按下 Ctrl+D 时,两个 netcat 实例都退出并且我返回到提示符。

这表明我可以毫无问题地建立 TCP 连接。接下来,我尝试了 UDP。

窗口 1:

$ nc -l -u 6900

窗口 2:

$ nc -u localhost 6900

很有趣。我想我只能输入“one”,按 Enter,然后输入“two”,再按 Enter,然后我就自动返回到窗口 2 中的提示符。

在窗口 1 上,我看不到任何输出。

-v在窗口 1 中重试:

$ nc -v -l -u 6900
Listening on [0.0.0.0] (family 0, port 6900)

-v当我在窗口 2 中重试时,有趣的是:

$ nc -v -u localhost 6900
$

就像 netcat 从来没有运行过一样;我立即返回到提示。

更新:如果我localhost用替换127.0.0.1,就会有进展。

当“服务器”在窗口 1 中监听时:

$ nc -v -l -u 6900
one
two
three

当我输入“one”、“two”和“three”时,我能够从窗口 2 获得回显的输出:

$ nc -u 127.0.0.1 6900
one
two
three
^C

-v仍然令人费解,因为它与上面的相同。

答案1

使用 macOS,我得出了不同的结论。关键是 IPv6。localhost解析为 IPv6 和 IPv4。但是,以下命令行将导致nc在 IPv4 上进行监听:

nc -l -u 6900

结果:

$ lsof -n -i:6900
COMMAND   PID  USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
nc      63923 fuzzy    3u  IPv4 0xad7f669b928a178b      0t0  UDP *:6900

现在,当你使用它来“连接”时:

nc -u localhost 6900

... 它实际上连接到 IPv6。当你使用127.0.0.1它时它不会。

但是,它无法连接,因为 UDP 是无连接的。因此,无法知道连接的远端是否真的存在。因此,它无法检测到是否应该回退到 IPv4。您的消息将被发送,但没有任何程序在监听这些消息。

发送时,可以观察到以下情况:

$ tcpdump -i lo0 udp port 6900
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
00:46:37.543273 IP6 localhost.54473 > localhost.6900: UDP, length 6

使用 TCP,它将尝试连接 IPv6,如果确定这不起作用,则使用 IPv4 重试:

$ tcpdump -i lo0 port 6900
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
00:50:21.495107 IP6 localhost.64306 > localhost.6900: Flags [SEW], seq 1432891337, win 65535, options [mss 16324,nop,wscale 5,nop,nop,TS val 766376063 ecr 0,sackOK,eol], length 0
00:50:21.495136 IP6 localhost.6900 > localhost.64306: Flags [R.], seq 0, ack 1432891338, win 0, length 0
00:50:21.495231 IP localhost.64307 > localhost.6900: Flags [S], seq 307818799, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 766376063 ecr 0,sackOK,eol], length 0
00:50:21.495283 IP localhost.6900 > localhost.64307: Flags [S.], seq 4254625238, ack 307818800, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 766376063 ecr 766376063,sackOK,eol], length 0
00:50:21.495295 IP localhost.64307 > localhost.6900: Flags [.], ack 1, win 12759, options [nop,nop,TS val 766376063 ecr 766376063], length 0
00:50:21.495306 IP localhost.6900 > localhost.64307: Flags [.], ack 1, win 12759, options [nop,nop,TS val 766376063 ecr 766376063], length 0

您可以强制nc使用 IPv4:

nc -4u localhost 6900

答案2

总结

结论在最后。


前言

我可以解释你的结果。我使用 Kubuntu,而不是 Debian,但我相信主要观点是相同的。要完全理解发生了什么,最好分析 TCP 情况,然后与 UDP 进行比较。

如果您想复制我的研究,请不要终止或杀死,nc除非我这样说,否则它会自动终止。nc当我说的时候杀死它。这非常重要。阅读完整答案后,您将了解这一点。

该分析需要多个终端(或tmux,或screen)。


TCP 案例

首先,摆脱所有先前的nc进程。

killall nc

运行监听进程:

nc -l 6900

检查 的输出lsof -i :6900。你的nc被列为监听进程。

运行连接进程:

nc localhost 6900

检查 的输出lsof -i :6900。两个ncs 是已确立的联系。

因为你的第一个程序nc不再监听,所以你可以运行第二聆听过程:

nc -l 6900

和一个第二连接过程:

nc localhost 6900

在每个运行的控制台中输入一些内容nc(不要忘记Enter每次都点击)。您会注意到有两个单独的连接。您可以建立一个第三如果你愿意的话。

再检查lsof -i :6900一下。虽然两个(以前)监听的ncs 使用相同的端口6900,但这两个连接不会混合,因为nc另一端的 s 使用不同的端口。当数据包到达6900端口时,内核会检查另一个端口并决定哪个(以前)监听的端口nc应该接收它。

您可以运行额外的(未配对的)连接nc

nc localhost 6900 || echo fail

它将立即失败。

您仍然建立了两个单独的连接。使用+或+终止nc每对中的一个,您会注意到相应的端点也会终止。这是因为 TCP 是面向连接的。当从任一端点正常终止连接时,另一端会收到通知,以便做出相应的反应 - 在这种情况下只是退出。CtrlCCtrlDnc


UDP 案例

重要的:

killall nc

按顺序运行以下命令,每个命令都在单独的控制台中运行。还要检查lsof -i :6900每个命令后会发生什么。命令如下:

nc -ul 6900
nc -u localhost 6900

在第一个(“聆听”)中输入一些内容nc(点击Enter);检查lsof -i :6900;在第二个中输入一些内容nc(点击Enter);lsof -i :6900再次检查并记录变化。

然后准备另一对(在单独的控制台中):

nc -ul 6900
nc -u localhost 6900

来回传递一些字符串,看看它是否有效。ncCtrl+终止一个字符串CCtrl+D不起作用),并注意另一个字符串nc(在相应的一端)仍在运行。“连接”的另一端不知道“连接”何时“终止”。正如您所看到的,lsof直到数据开始流动,它才知道“连接”已“建立”。

我在这里引用一些话,因为 UDP 是无连接的,这是主要的区别。


什么会变得复杂?

到目前为止,一切都应该顺利进行。现在是时候开始解释之前事情不顺利时的结果了。

重要的:

killall nc

准备一次“听力” nc

nc -ul 6900

检查lsof -i :6900。输出将如下所示:

… UDP *:6900

准备一个“连接” nc

nc -u localhost 6900

检查lsof -i :6900。示例输出(您的输出54766可能会有所不同):

… UDP *:6900
… UDP localhost:54766->localhost:6900

将一些数据从第二个传递nc到第一个。检查lsof -i :6900

… UDP localhost:6900->localhost:54766
… UDP localhost:54766->localhost:6900

第二个ncCtrl+结束C。再说一遍lsof -i :6900

… UDP localhost:6900->localhost:54766

这意味着第一个仅与“连接”另一端的nc端口“对话” 。当您尝试连接第三个时:54766nc

nc -u localhost 6900

它很可能会选择另一侧的随机端口。试一试。在终止之前,您将能够“传递”(大约)两行,就像您在问题中所做的那样nc

(注意:nc由于 ICMP 数据包而终止,请参阅wireshark;您可以像我一样捕获数据包。)

但是您可以强制使用正确的端口(更改54766以适合您的lsof输出):

nc -up 54766 localhost 6900

第四台服务器nc将能够传递数据!第一台服务器nc不会看到任何差异。就好像第二台服务器nc从未被终止过一样。

继续操作之前,请先终止第四个nc。让第一个继续运行。


选项-v

最后一个谜团是:为什么要nc -v -u localhost 6900立即退出?

在上面的例子中,我们有四个nc

  1. 首先– “聆听”一节;
  2. 第二– 成功连接,然后终止;
  3. 第三– 由于本地端口错误而无法连接;
  4. 第四个– 能够通过正确(强制)本地端口进行连接。

在我的 Kubuntu 中nc -uv …,如果它第二一个,传输几个X字符,可能是为了探测连接。因此,如果它第三第一,它不需要(大约)两行外部输入就会失败,它会立即失败。首先 nc仍在运行,尝试:

nc -uv localhost 6900

它应该会失败。同样,你可以强制正确的端口,它会像第四个 nc在上面的例子中。

nc -uvp 54766 localhost 6900

当我调用它时,我可以看到X-s 打印在控制台中首先 nc


打扫

killall nc

结论

在我看来,您已将(“监听”)nc与“连接”另一端的某个端口关联,这很可能是因为另一个nc(或其他东西)已成功传输了至少一个数据包。您的其他nc端口尝试使用其另一侧的其他端口,但无法与第一个端口通信。

如果我替换localhost127.0.0.1,就会有进展

无关紧要。我想在这种情况下,你只是开始清理,就像我们之后做的那样killall nc

相关内容