多台客户机如何同时连接到服务器上的一个端口(比方说80)?

我了解端口如何工作的基础知识。 然而,我没有得到的是多个客户端可以同时连接说端口80.我知道每个客户端都有一个独特的(为他们的机器)端口。 服务器是否从可用的端口回复到客户端,并简单说明回复来自80? 这个怎么用?

首先,“港口”只是一个数字。 所有“到端口的连接”实际上表示的是具有在其“目标端口”标题字段中指定的数目的数据包。

现在,您的问题有两个答案,一个用于有状态协议,一个用于无状态协议。

对于无状态协议(即UDP)来说,没有问题,因为“连接”不存在 – 多个人可以将数据包发送到同一个端口,并且数据包将以任何顺序到达。 没有人处于“连接”状态。

对于有状态协议(如TCP),连接通过由源和目标端口以及源和目标IP地址组成的4元组来标识。 因此,如果两台不同的机器连接到第三台机器上的同一端口,则有两个不同的连接,因为源IP不同。 如果同一台机器(或两个在NAT之后或共享相同的IP地址)两次连接到单个远程端,则通过源端口(通常是随机的高编号端口)区分连接。

简单地说,如果我连接到同一个Web服务器两次从我的客户端,这两个连接将从我的angular度来看有不同的源端口,从Web服务器的目标端口。 因此,即使两个连接具有相同的源IP地址和目的IP地址,也没有歧义。

端口是一种多路复用 IP地址的方式,以便不同的应用程序可以侦听相同的IP地址/协议对。 除非应用程序定义自己的更高级别的协议,否则无法复用端口。 如果使用相同协议的两个连接具有相同的源和目标IP以及相同的源和目标端口,则它们必须是相同的连接。

重要:

我很抱歉地说,来自“Borealid”的回应是不精确的,有些不正确的 – 首先与回答这个问题的有状态或无状态无关,最重要的是套接字的定义是不正确的。

首先记住以下两条规则:

  1. 套接字的主键:套接字由{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT, PROTOCOL}而不是由{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT}协议是套接字定义的重要组成部分。

  2. 操作系统进程和套接字映射:一个进程可以关联(可以打开/可以听)多个套接字,这可能是许多读者显而易见的。

示例1:连接到相同服务器端口的两个客户端表示: socket1 {SRC-A, 100, DEST-X,80, TCP}socket2{SRC-B, 100, DEST-X,80, TCP} 。 这意味着主机A连接到服务器X的端口80,另一个主机B也连接到同一个服务器X到同一个端口80.现在,服务器如何处理这两个套接字取决于服务器是单线程还是multithreading(I稍后解释)。 重要的是一台服务器可以同时监听多个套接字。

要回答这个post的原始问题:

无论有状态协议还是无状态协议,两个客户端可以连接到同一个服务器端口,因为对于每个客户端,我们可以分配一个不同的套接字(因为客户端IP肯定不同)。 相同的客户端也可以有两个连接到相同服务器端口的套接字 – 因为这样的套接字因SRC-PORT而异。 平心而论,“Borealid”基本上提到了同样正确的答案,但是提及无状态/全状态是一种不必要的/令人困惑的事情。

回答关于服务器如何知道哪个套接字应答的问题的第二部分。 首先明白,对于正在监听同一端口的单个服务器进程,可能有多个套接字(可能来自同一个客户端或来自不同的客户端)。 现在,只要服务器知道哪个请求与哪个套接字关联,就可以始终使用相同套接字对相应的客户端进行响应。 因此,服务器永远不需要在其自己的节点上打开另一个端口,而不是客户端最初试图绑定的原始端口。 如果任何服务器在绑定套接字后分配不同的服务器端口,那么在我看来,服务器正在浪费其资源,并且必须要求客户端连接再次绑定到分配的新端口。

多一点完整性:

例2:一个服务器的两个不同的进程可以监听同一个端口是非常有趣的问题。 如果你不认为协议作为参数定义套接字之一,那么答案是否定的。 这主要是因为我们可以说,在这种情况下,对于试图连接到服务器端口的单个客户端,将没有任何机制来提及客户端打算进行的两个监听过程中的哪一个。 这与(2)规定的主题是相同的。 然而,这是错误的答案,因为'协议'也是套接字定义的一部分。 这样,同一个节点中的两个进程只有在使用不同的协议时才能监听同一个端口。 例如,两个不相关的客户端(比如一个使用TCP而另一个使用UDP)可以绑定连接并传递到同一个服务器节点和同一个端口,但是它们必须由两个不同的服务器进程提供服务。

服务器types – 单一和多个:

当服务器的进程侦听意味着多个套接字的端口时,可以同时连接并与同一个服务器进程进行通信。 如果一个服务器只使用一个subprocess为所有套接字提供服务,那么服务器被称为单进程/线程服务器,如果服务器使用多个subprocess通过一个subprocess来服务每个套接字,则服务器被称为多进程/进程/线程服务器。 请注意,无论服务器的types如何,服务器都可以/应该总是使用相同的初始套接字来回应(不需要分配另一个服务器端口)。

如果可以的话,build议书籍和两卷的其余部分。

关于家长/子女程序的说明(回应“Ioan Alexandru Cucu”的查询/评论)

无论我提到任何关于两个过程的概念都说A和B,认为它们不是由父母亲关系相关的。 操作系统(特别是UNIX)通过devise允许subprocessinheritance父节点的所有文件描述符(FD)。 因此,一个进程A正在监听的所有套接字(在UNIX中也是OS的一部分也是FD的一部分)可以被更多的进程A1,A2 …聆听,只要它们通过与A的父子关系相关。一个独立的进程B(即与A没有父子关系)不能听相同的套接字。 另外还要注意的是,这个禁止两个独立进程监听同一个socket的规则存在于操作系统(或者它的networking库)上,而且大多数操作系统都会遵守这个规则。 但是,可以创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,这是“全部侦听”的代码。 一个简单的错误就是在地址127.0.0.1上监听,它只接受来自当前计算机的连接。 所以这不是一个连接,这只是意味着一个进程请求绑定()到端口IP,并且该进程负责处理到该端口的所有连接。 这暗示了每台计算机只能有一个进程监听端口的限制(有办法绕过使用多路复用,但这是一个更复杂的话题)。 如果Web服务器正在侦听端口80,则不能与其他Web服务器共享该端口。

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

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

这是一个简单的脚本( https://github.com/grokit/dcore/tree/master/apps/quicknet ),打开一个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)通信的subprocess。 父服务器切换到subprocess一个build立的套接字,通信回到客户端。

将数据发送到子级服务器的套接字时,操作系统中的TCP堆栈将创build一个返回到客户机的数据包,并将“从端口”设置为80。

多个客户端可以连接到服务器上的同一个端口(比如说80),因为在服务器端,在创build一个套接字绑定 (设置本地IP和端口)之后,套接字调用监听 ,告诉OS接受传入的连接。

当客户端尝试连接到端口80上的服务器时,将在服务器套接字上调用accept调用。 这将为正在尝试连接的客户端创build一个新的套接字,并且将为使用相同端口80的后续客户端创build类似的新套接字。

斜体字是系统调用。

参考

07wi-cs244b/refs/net2.pdf