首页 文章

多个客户端如何同时连接到服务器上的一个端口,比如80? [重复]

提问于
浏览
337

这个问题在这里已有答案:

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

5 回答

  • 316

    首先,“端口”只是一个数字 . 所有“与端口的连接”实际上表示的是具有在其“目标端口”头字段中指定的该数字的分组 .

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

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

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

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

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

  • 142

    Important:

    我很遗憾地说“Borealid”的回答是不精确的,有些不正确 - 首先,回答这个问题与有状态或无状态无关,最重要的是,套接字的元组定义是不正确的 .

    First remember below two rules:

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

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

    Example 1: 连接到同一服务器端口的两个客户端意味着: socket1 {SRC-A, 100, DEST-X,80, TCP}socket2{SRC-B, 100, DEST-X,80, TCP} . 这意味着主机A连接到服务器X 's port 80 and another host B also connects to same server X to the same port 80. Now, how the server handles these two sockets depends on if the server is single threaded or multiple threaded (I' ll稍后解释 . 重要的是一台服务器可以同时收听多个套接字 .

    To answer the original question of the post:

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

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

    A bit more for completeness:

    Example 2: 它's very interesting question that can a server'两个不同的进程监听同一个端口 . 如果您不将协议视为参数定义套接字之一,则答案为否 . 主动这是因为我们可以说,在这种情况下,尝试连接到服务器端口的单个客户端将没有任何机制来提及客户端打算进行的两个侦听过程中的哪一个 . 这与规则(2)所声称的主题相同 . 然而,这是错误的答案,因为'protocol'也是套接字定义的一部分 . 因此,只有在使用不同协议时,同一节点中的两个进程才能侦听同一端口 . 例如,两个不相关的客户端(比如一个使用TCP而另一个使用UDP)可以绑定连接和与同一服务器节点和同一端口进行通信,但它们必须由两个不同的服务器进程提供服务 .

    Server Types - single & multiple:

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

    如果可以,建议Books以及两卷的其余部分 .

    A Note on Parent/Child Process (in response to query/comment of 'Ioan Alexandru Cucu')

    无论我在哪里提到任何关于两个过程的概念,比如A和B,考虑它们与父子关系无关 . 操作系统(尤其是UNIX)允许子进程从父进程继承所有文件描述符(FD) . 因此,进程A监听的所有套接字(在UNIX中都是OS的一部分也是FD的一部分)可以被更多进程A1,A2,...监听,只要它们通过父子关系与A相关联 . 独立进程B(即与A没有父子关系)不能监听相同的套接字 . 此外,还要注意,禁止两个独立进程监听相同套接字的规则存在于操作系统(或其网络库)中,并且到目前为止大多数操作系统都遵守该规则 . 但是,可以创建自己的操作系统,这很可能违反这些限制 .

  • 24

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

    那么,当服务器侦听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,这是“全部监听”的代码 . 一个容易犯的错误是侦听地址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套接字,发送有效负载(在这种情况下为"Test payload."),等待几秒钟并断开连接 . 发生这种情况时再次执行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地址之间永远不会混淆 .

  • 4

    通常,对于每个连接客户端,服务器都会分叉与客户端(TCP)通信的子进程 . 父服务器将子进程切换到已 Build 的套接字,该套接字与客户端进行通信 .

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

  • 397

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

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

    斜体字是系统调用 .

    参考

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

相关问题