Answers:
一个服务器套接字监听在一个端口上。该服务器上的所有建立的客户端连接与同一侦听端口相关联在服务器端的连接。客户端和服务器端IP /端口对的组合唯一标识已建立的连接。只要同一服务器上的多个连接与不同的客户端 IP /端口对关联,它们就可以共享相同的服务器端 IP /端口对,并且该服务器将能够处理尽可能多的客户端(可用系统资源允许)至。
在客户端,通常的做法是,新的出站连接使用随机的客户端端口,在这种情况下,如果您在短时间内建立大量连接,则可能会耗尽可用端口。
那么,当服务器侦听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地址之间永远不会混淆。
已将连接的插座分配给新的(专用)端口
这是一种常见的直觉,但这是不正确的。未将连接的插座分配给新的/专用的端口。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
bind()
操作connect()
甚至在操作之前都是隐式的。
bind()
只在服务器端使用,accept()?
所以客户端也将绑定特定端口?
bind()
可以在客户端之前使用connect()
。
从理论上讲,是的。练习,不行。大多数内核(包括linux)不允许您bind()
使用已经分配的端口。允许这样做不是一个很大的补丁。
从概念上讲,我们应该区分socket和port。套接字是双向通信端点,即我们可以发送和接收字节的“事物”。这是一个概念性的事情,在名为“ socket”的数据包头中没有这样的字段。
端口是能够识别套接字的标识符。对于TCP,端口是16位整数,但是还有其他协议(例如,在unix套接字上,“端口”本质上是字符串)。
主要问题如下:如果传入数据包到达,则内核可以通过目标端口号识别其套接字。这是最常见的方式,但不是唯一的可能性:
因为您正在使用应用程序服务器,所以它将能够做到这一点。
bind()
。
bind()
?我可以想象,是的,这是完全有可能的,但事实是WinSock和Posix API都bind()
为此使用了调用,即使它们的参数化实际上是相同的。即使API没有此调用,您也需要以某种方式说出它,您要从哪里读取传入的字节。
listen()
/ accept()
API调用可以创建套接字,以使内核根据传入端口区分它们。OP的问题可以用他本质上要求的方式来解释。我认为这是很现实的,但这不是他的问题的字面意思。