在解决所述问题时这里,我尝试了一些非常基本的东西:我尝试通过 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
。两个nc
s 是已确立的联系。
因为你的第一个程序nc
不再监听,所以你可以运行第二聆听过程:
nc -l 6900
和一个第二连接过程:
nc localhost 6900
在每个运行的控制台中输入一些内容nc
(不要忘记Enter每次都点击)。您会注意到有两个单独的连接。您可以建立一个第三如果你愿意的话。
再检查lsof -i :6900
一下。虽然两个(以前)监听的nc
s 使用相同的端口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
来回传递一些字符串,看看它是否有效。nc
用Ctrl+终止一个字符串C(Ctrl+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
第二个nc
用Ctrl+结束C。再说一遍lsof -i :6900
:
… UDP localhost:6900->localhost:54766
这意味着第一个仅与“连接”另一端的nc
端口“对话” 。当您尝试连接第三个时:54766
nc
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
:
- 首先– “聆听”一节;
- 第二– 成功连接,然后终止;
- 第三– 由于本地端口错误而无法连接;
- 第四个– 能够通过正确(强制)本地端口进行连接。
在我的 Kubuntu 中nc -uv …
,如果它第二一个,传输几个X
字符,可能是为了探测连接。因此,如果它第三第一,它不需要(大约)两行外部输入就会失败,它会立即失败。首先 nc
仍在运行,尝试:
nc -uv localhost 6900
它应该会失败。同样,你可以强制正确的端口,它会像第四个 nc
在上面的例子中。
nc -uvp 54766 localhost 6900
当我调用它时,我可以看到X
-s 打印在控制台中首先 nc
。
打扫
killall nc
结论
在我看来,您已将(“监听”)nc
与“连接”另一端的某个端口关联,这很可能是因为另一个nc
(或其他东西)已成功传输了至少一个数据包。您的其他nc
端口尝试使用其另一侧的其他端口,但无法与第一个端口通信。
如果我替换
localhost
为127.0.0.1
,就会有进展
无关紧要。我想在这种情况下,你只是开始清理,就像我们之后做的那样killall nc
。