如何建立一个TCP连接返回相同的端口?

24

机器是RHEL 5.3(内核2.6.18)。

有时候我在netstat命令中注意到我的应用程序建立了TCP连接,当本地地址远程地址相同时。

在这里也有其他人报告了同样的问题。

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

这怎么可能呢?

编辑01

同时打开导致了问题(非常感谢Hasturkun)。您可以在传统的TCP状态图中看到它在从SYN_SENT状态到SYNC_RECEIVED状态的转换中。


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

18

这可能是由TCP同时连接引起的(在LKML的帖子中提到,也可以看看这里)。

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

在足够多次的尝试中,用于连接的套接字可能会绑定到与被连接的相同端口上,因为之前提到的同时连接而成功。这样你就神奇地把一个客户端连接到了自己。


我的客户端会不断尝试连接服务器,如果服务器宕机了怎么办?那么现在的问题是我该如何防止这种情况发生?这是否意味着服务器应该使用不在/proc/sys/net/ipv4/ip_local_port_range范围内的端口? - dimba
@dimba:是的,让服务器监听低于该范围的端口将完全防止此问题。 - Hasturkun
http://www.honeypots.net/misc/services 定义了端口范围。服务器应该使用“注册端口号”,而操作系统将从“动态端口号”范围中分配客户端端口(是否是由/proc/sys/net/ipv4/ip_local_port_range定义的范围?)? - dimba
@dimba: /proc/sys/net/ipv4/ip_local_port_range中的范围可以由用户设置,低于低值(在我的情况下为32768)的任何端口都将安全防护,以免发生意外连接。您不需要使用其中一个注册端口号(特别是如果您没有运行服务并注册了该端口)。 - Hasturkun

13

TCP连接由以下元组唯一标识:(本地地址,本地端口号,远程地址,远程端口号)。并没有要求本地地址远程地址必须不同或者端口号必须不同(尽管这可能非常奇怪)。但对于给定元组的相同值,最多只有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连接的一种方式是让连接的另一端同时尝试与你打开连接。这被称为同时SYN交换,链接到StackOverflow答案描述了这个过程。

我还有一篇关于使用同时SYN交换穿越NAT的论文,但在那种情况下,源和外部会完全不同。


明白了。但是客户端最终如何打开与服务器相同的端口(请参见帖子中的链接)?结果服务器无法打开它的服务器端口,因为它已经在使用中。 - dimba
据我所知,我没有故意从同一端口连接到同一端口。 - dimba
@dimba - 我有可以实现这个的代码了。为什么有人会这样做又是另一个问题了。但是这种做法确实是可行的。 - Omnifarious
真正奇怪的是,一个套接字代表两个端点,这可能需要操作系统进行非常特殊的处理。为什么他们觉得有必要费尽心思来支持这种用例呢? - ZhongYu
@ZhongYu - 因为 Linux。☺️ 说真的,一个好的开发者听到“那永远不可能发生”就会假设它会发生。一旦你意识到这种情况存在,就没有选择而必须支持它。任何其他行为都是不符合标准的错误行为。支持这种边缘情况是所有软件应该做的事情。这相当于在确保桥梁不会在有人驾车冲出边缘时坍塌的软件工程等效物。 - Omnifarious

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接