TCP:两个不同的套接字可以共享一个端口吗?


124

这可能是一个非常基本的问题,但使我感到困惑。

两个连接的插座可以共享一个端口吗?我正在编写一个应能够处理超过100k并发连接的应用程序服务器,并且我们知道系统上可用的端口数约为60k(16位)。已连接的套接字被分配给新的(专用)端口,因此这意味着并发连接数受端口数限制,除非多个套接字可以共享同一端口。所以这个问题。

我在这里先向您的帮助表示感谢!

Answers:


175

一个服务器套接字监听在一个端口上。该服务器上的所有建立的客户端连接与同一侦听端口相关联在服务器端的连接。客户端和服务器端IP /端口对的组合唯一标识已建立的连接。只要同一服务器上的多个连接与不同的客户端 IP /端口对关联,它们就可以共享相同的服务器 IP /端口对,并且该服务器将能够处理尽可能多的客户端(可用系统资源允许)至。

客户端,通常的做法是,新的出站连接使用随机的客户端端口,在这种情况下,如果您在短时间内建立大量连接,则可能会耗尽可用端口。


2
感谢您的回答,雷米!您的回答就是我好奇的一切。;)
KJ 2012年

2
如果我没有记错的话,@ Remy连接不仅可以通过源/目标端口/ IP来区分,还可以通过协议(TCP,UDP等)来区分。
Ondrej Peterka 2014年

1
@OndraPeterka:是的,但是并非所有平台都对此有所限制。例如,Windows可以愉快地允许单独的IPv4和IPv6服务器套接字在同一本地IP:Port上侦听,而不会跳过箍,但是* Nix系统(包括Linux和Android)不允许。
雷米·勒博2014年

6
@ user2268997:您不能使用单个套接字连接到多个服务器。您必须为每个连接创建一个单独的套接字。
雷米·勒博2015年

3
@FernandoGonzalezSanchez:一个客户端可以将多个TCP套接字绑定到同一本地IP /端口对,只要它们连接到不同的远程IP /端口对即可。这不是特定于Windows的,这是TCP总体工作方式的一部分。
人头马(Remy Lebeau)

182

端口上的TCP / HTTP侦听:许多用户如何共享同一端口

那么,当服务器侦听TCP端口上的传入连接时会发生什么?例如,假设您在端口80上有一个Web服务器。假设您的计算机的公共IP地址为24.14.181.229,而尝试连接到您的人的IP地址为10.1.2.3。此人可以通过打开24.14.181.229:80的TCP套接字来连接到您。很简单。

直观地(错误地),大多数人认为它看起来像这样:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

这很直观,因为从客户端的角度来看,他拥有IP地址,并通过IP:PORT连接到服务器。由于客户端连接到端口80,那么他的端口也必须也是80吗?这是一件明智的事情,但实际上并非所发生的事情。如果正确,那么每个外部IP地址只能为一个用户提供服务。一旦远程计算机连接,他就将端口80连接到端口80的连接,其他任何人都无法连接。

必须了解三件事:

1.)在服务器上,进程正在侦听端口。一旦获得连接,它将把它交给另一个线程。通信从不占用监听端口。

2.)连接由操作系统通过以下5个元组唯一标识:(本地IP,本地端口,远程IP,远程端口,协议)。如果元组中的任何元素不同,则这是一个完全独立的连接。

3.)当客户端连接到服务器时,它会选择一个随机的,未使用的高阶源端口。这样,单个客户端最多可以在同一目标端口上与服务器建立约64k的连接。

因此,这实际上是客户端连接到服务器时创建的内容:

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

看实际上发生了什么

首先,让我们使用netstat查看这台计算机上正在发生的事情。我们将使用端口500而不是端口80(因为在端口80上发生了很多事情,因为它是一个通用端口,但是在功能上没有什么区别)。

    netstat -atnp | grep -i ":500 "

如预期的那样,输出为空白。现在让我们启动一个Web服务器:

    sudo python3 -m http.server 500

现在,这是再次运行netstat的输出:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

因此,现在有一个进程正在端口500上积极侦听(状态:LISTEN)。本地地址是0.0.0.0,这是“侦听所有ip地址”的代码。一个容易犯的错误是仅侦听端口127.0.0.1,该端口仅接受来自当前计算机的连接。所以这不是一个连接,这仅意味着一个进程请求将bind()绑定到端口IP,并且该进程负责处理与该端口的所有连接。这暗示了局限性,即每台计算机在一个端口上侦听只能有一个进程(有多种方法可以使用多路复用来解决该问题,但这是一个复杂得多的主题)。如果Web服务器正在侦听端口80,则它无法与其他Web服务器共享该端口。

现在,让我们将用户连接到我们的机器:

    quicknet -m tcp -t localhost:500 -p Test payload.

这是一个简单的脚本(https://github.com/grokit/quickweb),它打开一个TCP套接字,发送有效负载(在这种情况下为“测试有效负载”。),等待几秒钟并断开连接。再次执行netstat时,将显示以下内容:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

如果您与另一个客户端连接并再次执行netstat,您将看到以下内容:

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

...也就是说,客户端使用了另一个随机端口进行连接。因此,IP地址之间永远不会混淆。


11
这是我在SO上见过的最好的答案。
乔布斯

1
@ N0thing“通过这种方式,单个客户端最多可以在同一目标端口上与服务器建立约64k的连接。” 因此,在实践中,如果客户端没有同时连接两次或多次连接到同一服务器和端口,则客户端的连接数甚至可能超过〜64K。真的吗。如果是,则表示从客户端的单个端口可以连接到许多不同的服务器进程(以使套接字连接不同)。因此,总的来说,多个客户端套接字可以驻留在客户端计算机的同一端口上吗?请阅读我对“ Remey Lebeau”答案的评论。谢谢:D
Prem KTiw '16

6
@premktiw:是的,如果多个客户端套接字连接到不同的服务器IP /端口对,则可以同时将它们绑定到同一本地IP /端口对,因此本地+远程对的元组是唯一的。是的,客户端可能总共拥有超过64K的并发连接。只要服务器IP /端口对是唯一的,它就可以从单个端口连接到可能无限数量的服务器(受可用OS资源,可用路由器端口等限制)。
雷米·勒博

1
@RemyLebeau满意。非常感谢您:D
Prem KTiw

1
@bibstha当所有传入连接都被拒绝时,防火墙如何处理随机端口?
PatrykG

35

已将连接的插座分配给新的(专用)端口

这是一种常见的直觉,但这是不正确的。未将连接的插座分配给新的/专用的端口。TCP堆栈必须满足的唯一实际约束是,每个套接字连接的元组(local_address,local_port,remote_address,remote_port)必须唯一。因此,只要端口上的每个套接字都连接到不同的远程位置,服务器就可以具有使用同一本地端口的许多TCP套接字。

请参阅以下网址中的“套接字对”段落:http : //books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq= socket%20pair% 20tuple&pg=PA52#v=onepage&q= socket%20pair% 20tuple&f=false


1
杰里米,谢谢您的完美回答!
2012年

6
您所说的完全对服务器端而言。但是,BSD套接字API的结构意味着,实际上,传出的客户端端口必须是唯一的,因为该bind()操作connect()甚至在操作之前都是隐式的。
罗恩侯爵,

1
@EJP嗨,我以为bind()只在服务器端使用,accept()?所以客户端也将绑定特定端口?
GMsoF

5
@GMsoF:bind()可以在客户端之前使用connect()
Remy Lebeau

10

从理论上讲,是的。练习,不行。大多数内核(包括linux)不允许您bind()使用已经分配的端口。允许这样做不是一个很大的补丁。

从概念上讲,我们应该区分socketport。套接字是双向通信端点,即我们可以发送和接收字节的“事物”。这是一个概念性的事情,在名为“ socket”的数据包头中没有这样的字段。

端口是能够识别套接字的标识符。对于TCP,端口是16位整数,但是还有其他协议(例如,在unix套接字上,“端口”本质上是字符串)。

主要问题如下:如果传入数据包到达,则内核可以通过目标端口号识别其套接字。这是最常见的方式,但不是唯一的可能性:

  • 套接字可以通过传入数据包的目标IP进行标识。例如,如果我们有一个服务器同时使用两个IP,就是这种情况。然后,我们可以在相同的端口上但在不同的IP上运行不同的Web服务器。
  • 套接字也可以通过其端口和ip 进行标识。在许多负载平衡配置中就是这种情况。

因为您正在使用应用程序服务器,所以它将能够做到这一点。


2
他没问要再做一件事bind()
洛恩侯爵(Marques of Lorne)

1
@ user207421您是否见过没有设置侦听套接字的操作系统bind()?我可以想象,是的,这是完全有可能的,但事实是WinSock和Posix API都bind()为此使用了调用,即使它们的参数化实际上是相同的。即使API没有此调用,您也需要以某种方式说出它,您要从哪里读取传入的字节
peterh-恢复莫妮卡

1
@ user207421当然,可以使用相同的端口处理100k或更多的TCP连接,listen()/ accept()API调用可以创建套接字,以使内核根据传入端口区分它们。OP的问题可以用他本质上要求的方式来解释。我认为这是很现实的,但这不是他的问题的字面意思。
peterh-恢复莫妮卡

1

不能。在特定时刻不能共享同一端口。但是您可以将您的应用程序制作为使其能够在不同时刻进行端口访问。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.