我了解端口工作的基本原理。然而,我不明白的是多个客户端如何同时连接到端口80。我知道每个客户端都有一个唯一的端口(用于他们的机器)。服务器是否从一个可用端口向客户端返回,并简单地声明该响应来自80?这是如何工作的呢?


当前回答

多个客户端可以连接到服务器上的同一个端口(比如80),因为在服务器端,在创建套接字并绑定(设置本地IP和端口)后,在套接字上调用listen,它告诉操作系统接受传入的连接。

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

斜体字是系统调用。

Ref

http://www.scs.stanford.edu/07wi-cs244b/refs/net2.pdf

其他回答

多个客户端可以连接到服务器上的同一个端口(比如80),因为在服务器端,在创建套接字并绑定(设置本地IP和端口)后,在套接字上调用listen,它告诉操作系统接受传入的连接。

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

斜体字是系统调用。

Ref

http://www.scs.stanford.edu/07wi-cs244b/refs/net2.pdf

TCP / HTTP端口监听:多个用户如何共享同一个端口

那么,当服务器侦听TCP端口上的传入连接时会发生什么呢?例如,假设您在端口80上有一个web服务器。让我们假设您的计算机具有24.14.181.229的公共IP地址,而试图连接您的人具有10.1.2.3的IP地址。此人可以通过打开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元组(local-IP, local-port, remote-IP, remote-port, protocol)唯一标识连接。如果元组中的任何元素是不同的,那么这是一个完全独立的连接。

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      - 

So now there is one process that is actively listening (State: LISTEN) on port 500. The local address is 0.0.0.0, which is code for "listening for all". An easy mistake to make is to listen on address 127.0.0.1, which will only accept connections from the current computer. So this is not a connection, this just means that a process requested to bind() to port IP, and that process is responsible for handling all connections to that port. This hints to the limitation that there can only be one process per computer listening on a port (there are ways to get around that using multiplexing, but this is a much more complicated topic). If a web-server is listening on port 80, it cannot share that port with other web-servers.

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

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

这是一个简单的脚本(https://github.com/grokit/dcore/tree/master/apps/quicknet),它打开一个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地址之间永远不会混淆。

重要的是:

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

首先记住以下两条规则:

套接字的主键:一个套接字由{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT, PROTOCOL}来标识,而不是由{SRC-IP, SRC-PORT, DEST-IP, DEST-PORT}来标识。 OS进程和套接字映射:一个进程可以与(可以打开/可以监听)多个套接字相关联,这对许多读者来说可能是显而易见的。

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

回答文章最初的问题:

不管有状态协议还是无状态协议,两个客户端都可以连接到同一个服务器端口,因为我们可以为每个客户端分配不同的套接字(因为客户端IP肯定会不同)。同一个客户端也可以有两个连接到同一个服务器端口的套接字——因为这些套接字因SRC-PORT的不同而不同。公平地说,“Borealid”基本上提到了相同的正确答案,但提到无状态/完整有点不必要/令人困惑。

To answer the second part of the question on how a server knows which socket to answer. First understand that for a single server process that is listening to same port, there could be more than one sockets (may be from same client or from different clients). Now as long as a server knows which request is associated with which socket, it can always respond to appropriate client using the same socket. Thus a server never needs to open another port in its own node than the original one on which client initially tried to connect. If any server allocates different server-ports after a socket is bound, then in my opinion the server is wasting its resource and it must be needing the client to connect again to the new port assigned.

为了完整起见:

Example 2: It's a very interesting question: "can two different processes on a server listen to the same port". If you do not consider protocol as one of parameter defining socket then the answer is no. This is so because we can say that in such case, a single client trying to connect to a server-port will not have any mechanism to mention which of the two listening processes the client intends to connect to. This is the same theme asserted by rule (2). However this is WRONG answer because 'protocol' is also a part of the socket definition. Thus two processes in same node can listen to same port only if they are using different protocol. For example two unrelated clients (say one is using TCP and another is using UDP) can connect and communicate to the same server node and to the same port but they must be served by two different server-processes.

服务器类型-单个和多个:

When a server's processes listening to a port that means multiple sockets can simultaneously connect and communicate with the same server-process. If a server uses only a single child-process to serve all the sockets then the server is called single-process/threaded and if the server uses many sub-processes to serve each socket by one sub-process then the server is called multi-process/threaded server. Note that irrespective of the server's type a server can/should always uses the same initial socket to respond back (no need to allocate another server-port).

建议书籍和剩下的两卷如果可以的话。

关于父/子过程的说明(回复“Ioan Alexandru Cucu”的查询/评论)

Wherever I mentioned any concept in relation to two processes say A and B, consider that they are not related by parent child relationship. OS's (especially UNIX) by design allow a child process to inherit all File-descriptors (FD) from parents. Thus all the sockets (in UNIX like OS are also part of FD) that a process A listening to, can be listened by many more processes A1, A2, .. as long as they are related by parent-child relation to A. But an independent process B (i.e. having no parent-child relation to A) cannot listen to same socket. In addition, also note that this rule of disallowing two independent processes to listen to same socket lies on an OS (or its network libraries) and by far it's obeyed by most OS's. However, one can create own OS which can very well violate this restrictions.

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

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

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

For a stateful protocol (like TCP), a connection is identified by a 4-tuple consisting of source and destination ports and source and destination IP addresses. So, if two different machines connect to the same port on a third machine, there are two distinct connections because the source IPs differ. If the same machine (or two behind NAT or otherwise sharing the same IP address) connects twice to a single remote end, the connections are differentiated by source port (which is generally a random high-numbered port).

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

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

通常,对于每个连接的客户端,服务器都派生出一个与客户端通信的子进程(TCP)。父服务器将已建立的套接字传递给子进程,该套接字与客户端进行通信。

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