【发布时间】:2011-08-07 07:25:16
【问题描述】:
我想不出确切的套接字 api,但我记得有一个套接字选项可以演示端口独占/非独占。
如果不是独占的,TCP如何知道它应该为特定的目标端口转发到哪个应用程序?
【问题讨论】:
标签: c network-programming
我想不出确切的套接字 api,但我记得有一个套接字选项可以演示端口独占/非独占。
如果不是独占的,TCP如何知道它应该为特定的目标端口转发到哪个应用程序?
【问题讨论】:
标签: c network-programming
我认为您可能指的是某些系统上可用的SO_REUSEPORT 选项。
来自 BSD 手册页:
SO_REUSEPORT 允许多个进程完全重复绑定 如果他们在绑定端口之前都设置了 SO_REUSEPORT 。此选项允许程序的多个实例分别接收 UDP/IP 多播或 广播发往绑定端口的数据报。
对此的实现有很大的不同(从不存在到限制为 UDP 到也允许 TCP)。在允许 TCP 的情况下,连接由 both 源和目标 (ip,port) 对来区分。这足以让实现决定哪个应用程序需要哪个数据包。 (例如,参见Trek - Socket options。)
多个应用绑定到同一个 TCP 端口时,您只能在该端口上拥有一个套接字 accepting。其他人将使用该端口启动出站连接。 TCP 堆栈总是知道将数据包发送到哪里。
注意:套接字本身(包括,我相信接受套接字)可以在多个进程之间共享。例如,请参阅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
在这里,可能会发生三件事:
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
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)]
现在可以进来四种数据包:
TCP 堆栈总是现在知道数据包属于哪个套接字。所以它知道将数据传递到哪里。
SO_REUSEADDR 不同:它只允许绑定到处于TIME_WAIT 状态的端口 - 即端口已经关闭,打开它的套接字已经发出close(或多或少,还有其他条件)。使用SO_REUSEADDR,一次只有一个套接字保持打开状态。
【讨论】:
套接字由 IP 地址和端口标识 - 两者都有。在 BSD 套接字 API 中,它们一起被称为“名称”。您可以将套接字绑定到一个名称,如果该名称包含特定地址,您可以将另一个套接字绑定到具有相同端口的另一个名称。
另外:connection 是一对两个套接字,因此单个地址/端口对可以用于多个连接 - 这意味着多个 connected 套接字可以具有同名(但不是同一个 fd)。
【讨论】: