TCP:两个不同的套接字可以共享一个端口吗?
这可能是一个非常基本的问题,但它使我感到困惑。
两个不同的连接sockets可以共享一个端口吗? 我正在编写一个能够处理超过10万个并发连接的应用程序服务器,并且我们知道系统上可用端口的数量大约为60k(16位)。 一个连接的套接字被分配给一个新的(专用)端口,所以这意味着并发连接的数量受到端口数量的限制,除非多个套接字可以共享相同的端口。 所以这个问题。
我在这里先向您的帮助表示感谢!
服务器套接字在单个端口上侦听。 该服务器上的所有已build立的客户端连接都与连接的服务器端上的相同侦听端口相关联。 已build立的连接由客户端和服务器端IP /端口对的组合唯一标识。 同一服务器上的多个连接可以共享相同的服务器端 IP /端口对,只要它们与不同的客户端 IP /端口对相关联,并且服务器将能够处理尽可能多的客户端,因为可用的系统资源允许它至。
在客户端 ,新的出站连接通常使用随机的客户端端口,在这种情况下,如果在短时间内build立大量连接,可能会用尽可用的端口。
TCP / HTTP侦听端口:许多用户如何共享相同的端口
那么,当服务器侦听TCP端口上的传入连接时会发生什么? 例如,假设您在端口80上有一台Web服务器。假设您的计算机的公有IP地址为24.14.181.229,而试图连接到您的人的IP地址为10.1.2.3。 此人可以通过打开一个TCP套接字连接到你24.14.181.229:80。 很简单。
直觉(错误地),大多数人认为它看起来像这样:
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.
这是直观的,因为从客户端的angular度来看,他有一个IP地址,并连接到IP:PORT的服务器。 由于客户端连接到端口80,那么他的端口也必须是80? 这是一个明智的想法,但实际上不会发生什么事情。 如果这是正确的,我们只能为每个外部IP地址提供一个用户。 一旦远程计算机连接,然后他将端口80端口80连接,并没有其他人可以连接。
必须理解三件事:
1.)在服务器上,进程正在侦听端口。 一旦获得连接,它将它交给另一个线程。 沟通从来没有听过监听端口。
2.)连接由操作系统通过以下5元组唯一标识:(本地IP,本地端口,远程IP,远程端口,协议)。 如果元组中的元素不同,那么这是一个完全独立的连接。
3.)当一个客户端连接到一个服务器时,它挑选一个随机的,未使用的高阶源端口 。 这样,一个客户端可以有多达64k的连接到相同的目的地端口的服务器。
所以,这实际上是当客户端连接到服务器时创build的:
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上发生了一大堆事情,因为它是一个普通的端口,但function上并没有什么区别)。
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 -
因此,现在有一个进程正在主动侦听(状态:LISTEN)500端口。本地地址是0.0.0.0,这是“侦听所有IP地址”的代码。 一个简单的错误就是只能监听端口127.0.0.1,它只接受来自当前计算机的连接。 所以这不是一个连接,这只是意味着一个进程请求绑定()到端口IP,并且该进程负责处理到该端口的所有连接。 这暗示了每台计算机只能有一个进程监听端口的限制(有办法绕过使用多路复用,但这是一个更复杂的话题)。 如果Web服务器正在侦听端口80,则不能与其他Web服务器共享该端口。
现在,让我们将用户连接到我们的机器:
quicknet -m tcp -t localhost:500 -p Test payload.
这是一个简单的脚本( https://github.com/grokit/quickweb ),它打开一个TCP套接字,发送有效载荷(在本例中为“testing载荷”),等待几秒钟并断开连接。 发生这种情况时再次执行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地址之间永远不会混淆。
连接的套接字被分配给新的(专用)端口
这是一个普遍的直觉,但这是不正确的。 连接的套接字未分配给新的/专用端口。 TCP堆栈必须满足的唯一实际约束是(local_address,local_port,remote_address,remote_port)的元组对于每个套接字连接必须是唯一的。 因此,只要端口上的每个套接字连接到不同的远程位置,服务器就可以拥有多个使用相同本地端口的TCP套接字。
请参阅以下url的“套接字对”: http : //books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq= socket%20pair% 20tuple&pg=PA52#v=onepage&q= socket%20pair% 20tuple&f=false
理论上,是的。 练习,而不是。 大多数内核(包括linux)不允许你第二个bind()
到已经分配的端口。 这是不是一个真正的大补丁,使这个允许。
在概念上,我们应该区分套接字和端口 。 套接字是双向通信端点,即我们可以发送和接收字节的“事物”。 这是一个概念性的东西,在名为“socket”的包头中没有这样的字段。
端口是能够识别套接字的标识符。 在TCP的情况下,一个端口是一个16位整数,但也有其他协议(例如,在Unix套接字上,“端口”本质上是一个string)。
主要问题如下:如果一个传入数据包到达,内核可以通过目标端口号识别它的套接字。 这是最常见的方式,但不是唯一的可能性:
- 套接字可以由传入数据包的目标IP标识。 例如,如果我们有一个服务器同时使用两个IP,就是这种情况。 然后,我们可以在相同的端口上运行不同的Web服务器,但在不同的IP上。
- 套接字可以通过它们的源端口和ip来识别。 许多负载平衡configuration中就是这种情况。
因为你正在使用一个应用程序服务器,它将能够做到这一点。