你怎么能有一个 TCP 连接回到同一个端口?

Posted

技术标签:

【中文标题】你怎么能有一个 TCP 连接回到同一个端口?【英文标题】:How can you have a TCP connection back to the same port? 【发布时间】:2011-06-24 09:44:44 【问题描述】:

机器是 RHEL 5.3(内核 2.6.18)。

有时我在 netstat 中注意到我的应用程序有连接,当 本地地址外国地址 相同时建立 TCP 连接。

Here 其他人也报告了同样的问题。

症状与链接中描述的相同 - 客户端连接到本地运行的服务器的端口 X 端口。一段时间后,netstat 显示客户端有从127.0.0.1:X127.0.0.1:X 的连接

这怎么可能?

编辑 01

同时打开导致问题(非常感谢 Hasturkun)。你可以在classical TCP state diagram 上看到它正在从 SYN_SENT 状态转换到 SYNC_RECEIVED

【问题讨论】:

症状与链接中描述的相同 - 客户端连接到本地运行的服务器的端口X端口。一段时间后,netstat 显示客户端已从 127.0.0.1:X 连接到 127.0.0.1:X 【参考方案1】:

这可能是由 TCP 同时连接引起的(提到了on this post to LKML,另见here)。

在尝试连接到动态本地端口范围内的端口(可以在/proc/sys/net/ipv4/ip_local_port_range 中看到)时,程序循环可能会在服务器未侦听该端口时成功。

如果尝试次数足够多,则用于连接的套接字可能会绑定到正在连接的同一端口,由于前面提到的同时连接,该端口可能会成功。你现在神奇地有一个连接到自身的客户端

【讨论】:

确实,如果服务器关闭,我的客户端会不断尝试连接到服务器。那么现在的问题是如何防止这种情况发生?这是否意味着服务器应该使用不在 /proc/sys/net/ipv4/ip_local_port_range 范围内的端口? @dimba: 是的,让服务器监听低于该范围低端的端口将完全防止这个问题 honeypots.net/misc/services 定义端口范围。服务器是否应该“注册端口号”,而操作系统将从“动态端口号”范围内分配客户端端口(它是由 /proc/sys/net/ipv4/ip_local_port_range 定义的范围吗?)? @dimba:/proc/sys/net/ipv4/ip_local_port_range 中的范围是用户可设置的,任何低于低值(在我的情况下为32768)的东西都可以防止这种意外连接。您没有必须使用其中一个注册端口号(尤其是如果您没有运行在该端口上注册的服务)【参考方案2】:

一个 TCP 连接由这个元组 (local address, local port #, foreign address, foreign port #) 唯一标识。不需要local addressforeign address,甚至不需要端口号不同(尽管那会非常奇怪)。但是对于给定的元组,最多有 1 个 TCP 连接具有相同的值。

当一台计算机连接到自己时,它的本地地址和外部地址几乎总是相同的。毕竟,“本地”端和“外国”端实际上是同一台计算机。事实上,当这种情况发生时,您的计算机应该显示两个具有相同“本地”和“外部”地址但端口号相反的连接。例如:

$ ssh localhost

将产生两个如下所示的连接:

$ netstat -nA inet | fgrep :22
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address        Foreign Address        State      
tcp        0      0 127.0.0.1:56039      127.0.0.1:22           ESTABLISHED 
tcp        0      0 127.0.0.1:22         127.0.0.1:56039        ESTABLISHED 

如您所见,本地地址和外部地址相同,但端口号相反。此 TCP 连接的唯一元组是 (127.0.0.1, 56039, 127.0.0.1, 22)。不会有其他 TCP 连接具有这四个字段。

您看到两个的事实是因为您的计算机是连接的两端。每一端都有自己的观点,哪个是“外国”,哪个是“本地”。

您甚至可以在同一个端口上连接到自己,虽然这并不常见,但规范并未禁止。这是一个 Python 示例程序,它将执行此操作:

import socket
import time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 56443))
s.connect(('127.0.0.1', 56443))
time.sleep(30)

此代码有效,因为可以打开 TCP 连接的一种方法是让连接的另一端尝试与您同时打开一个。这被称为simultaneous SYN exchange,链接到 *** 的答案描述了它的含义。

我也有一篇关于using simultaneous SYN exchange to get through NAT 的论文,不过在那种情况下,来源和外国会完全不同。

【讨论】:

明白。但是客户端最终如何打开服务器的相同端口(参见帖子中的链接)?结果服务器无法打开它的服务器端口,因为它已在使用中。 AFAIK,我不是故意从同一个端口连接到同一个端口 @dimba - 我现在有代码显示如何实现它。为什么有人会做这样的事情是另一个问题。但这当然是可能的。 真正奇怪的是一个socket代表了两端,这可能需要OS进行非常特殊的处理。为什么他们觉得有必要不遗余力地支持这个用例? @ZhongYu - 因为 Linux。 ☺️说真的,优秀的开发人员听到“这永远不会发生”并假设它会发生。一旦你意识到这个案例的存在,除了支持它别无选择。其他任何事情都是不符合标准的不正确行为。支持这种边缘情况是所有软件都应该做的。软件工程相当于确保有人开车驶离边缘时您的桥梁不会倒塌。

以上是关于你怎么能有一个 TCP 连接回到同一个端口?的主要内容,如果未能解决你的问题,请参考以下文章

如何将TCP连接重新连接到同一端口?

已知IP和端口,用c语言怎么判断能不能连接网络

TCP socket调试工具怎么建虚拟接收端

晕,Spotlight怎么连接

TCP有235端口,UDP也可以有235端口,为啥不会冲突?

LISTENING,SYN_SENT,ESTABLISHED等TCP端口状态意思