【问题标题】:How can non-exclusive ports exist when tcp identify applications using 16-bit port number?tcp 使用 16 位端口号识别应用程序时,非独占端口如何存在?
【发布时间】:2011-08-07 07:25:16
【问题描述】:

我想不出确切的套接字 api,但我记得有一个套接字选项可以演示端口独占/非独占。

如果不是独占的,TCP如何知道它应该为特定的目标端口转发到哪个应用程序?

【问题讨论】:

    标签: c network-programming


    【解决方案1】:

    我认为您可能指的是某些系统上可用的SO_REUSEPORT 选项。

    来自 BSD 手册页:

    SO_REUSEPORT 允许多个进程完全重复绑定 如果他们在绑定端口之前都设置了 SO_REUSEPORT 。此选项允许程序的多个实例分别接收 UDP/IP 多播或 广播发往绑定端口的数据报。

    对此的实现有很大的不同(从不存在到限制为 UDP 到也允许 TCP)。在允许 TCP 的情况下,连接由 both 源和目标 (ip,port) 对来区分。这足以让实现决定哪个应用程序需要哪个数据包。 (例如,参见Trek - Socket options。)

    多个应用绑定到同一个 TCP 端口时,您只能在该端口上拥有一个套接字 accepting。其他人将使用该端口启动出站连接。 TCP 堆栈总是知道将数据包发送到哪里。

    • 启动 (SYN) 连接的传入数据包会转到唯一接受的套接字
    • 已连接流的传入数据包被路由到它们所属的套接字

    注意:套接字本身(包括,我相信接受套接字)可以在多个进程之间共享。例如,请参阅Is there a way for multiple processes to share a listening socket?


    这是它的工作原理。自愿简化 TCP(无三次握手)。让我们像这样记录TCP栈持有的socket信息

    (socketname)[owner app, (local IP, local port), (state, remote IP, remote port)]
    

    这样,让我们​​设置三个应用程序 A、B 和 C:

    App A -> bind (localhost,12345,SO_REUSEPORT)
        TCP stack: create socket (s1)[belongs to A, (localhost,12345), (not connected)]
    App A <- s1
    
    App B -> bind (localhost,12345,SO_REUSEPORT)
        TCP stack: create socket (s2)[belongs to B, (localhost,12345), (not connected)]
    App B <- s2
    
    App C -> bind (localhost,12345,SO_REUSEPORT)
        TCP stack: create socket (s3)[belongs to C, (localhost,12345), (not connected)]
    App C <- s3
    App C -> s3.listen()
        TCP stack: update socket (s3)[belongs to C, (localhost,12345), (open for business)]
    App C -> s3.accept()
    

    此时,还没有发送任何数据,但是内核确切地知道哪个套接字属于哪个应用程序。让我们让 A 实际上尝试用它的套接字做一些事情:

    App A -> s1.connect(otherhost,54321)
        TCP stack: update socket (s1)[belongs to A, (localhost,12345), (connecting, otherhost,54321)]
        TCP stack: send SYN
    

    在这里,可能会发生三件事:

  • 来自 (otherhost,54321) 的传入 ACK 数据包具有正确的顺序:这很好,它用于 s1,没有其他可能性
        TCP stack: update socket (s1)[belongs to A, (localhost,12345), (connected,otherhost,54321)]
        TCP stack: send SYN/ACK
        TCP stack: notify App A that socket is connected
    App A <- connect succeeded, you can start doin' stuff
    

  • 来自 (client,4343) 的传入 SYN 数据包:只能用于 s3,只能用于 SYN 的套接字
        TCP stack: create new socket (s4)[belongs to C, (localhost,12345), (connected,client,4343)]
        TCP stack: send ACK to client
        TCP stack: notify App C that (s4) has been accepted
    App C <- s4 returned from accept()
    

  • 从其他地方传入的数据包:
        TCP stack: drop or reject, there are no matching sessions
    

    让我们想象上面发生的两件事。内核信息现在是:

    (s1)[belongs to A, (localhost,12345), (connected,otherhost,54321)]
    (s2)[belongs to B, (localhost,12345), (not connected)]
    (s3)[belongs to C, (localhost,12345), (open for business)]
    (s4)[belongs to C, (localhost,12345), (connected,client,4343)]
    

    现在可以进来四种数据包:

  • 来自 (otherhost,54321) 的传入正常数据包:与 s1 匹配,将其交给 App A
  • 来自 (client,4343) 的传入正常数据包:这与 s4 匹配,将其交给 App C
  • 来自 (otherclient,2398) 的传入 SYN 数据包:这匹配 s3,与之前相同
  • 其他:丢弃或拒绝,无效状态

    TCP 堆栈总是现在知道数据包属于哪个套接字。所以它知道将数据传递到哪里。


    SO_REUSEADDR 不同:它只允许绑定到处于TIME_WAIT 状态的端口 - 即端口已经关闭,打开它的套接字已经发出close(或多或少,还有其他条件)。使用SO_REUSEADDR,一次只有一个套接字保持打开状态。

  • 【讨论】:

    • TCP 如何知道它应该为特定目标端口转发到哪个应用程序?
    • 这不是选择应用程序的问题,而是选择正确的套接字的问题,这对于源和目标(IP/端口)对都是可行的。什么应用程序使用哪个套接字并不是一个真正的问题,就像其他 I/O 方面一样处理(即在类 Unix 平台上打开文件)
    【解决方案2】:

    套接字由 IP 地址和端口标识 - 两者都有。在 BSD 套接字 API 中,它们一起被称为“名称”。您可以将套接字绑定到一个名称,如果该名称包含特定地址,您可以将另一个套接字绑定到具有相同端口的另一个名称。

    另外:connection 是一对两个套接字,因此单个地址/端口对可以用于多个连接 - 这意味着多个 connected 套接字可以具有同名(但不是同一个 fd)。

    【讨论】:

    • 你能举几个例子吗?
    猜你喜欢
    • 2013-06-30
    • 2014-08-09
    • 2012-01-04
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-02
    • 2018-12-27
    相关资源
    最近更新 更多