首页 文章

TCP:两个不同的套接字可以共享一个端口吗?

提问于
浏览
70

这可能是一个非常基本的问题,但它让我感到困惑 .

两个不同的连接插座可以共用一个端口吗?我正在编写一个应该能够处理超过10万个并发连接的应用服务器,我们知道系统上可用的端口数量大约是60k(16位) . 连接的套接字被分配给新的(专用)端口,因此这意味着并发连接的数量受端口数量的限制,除非多个套接字可以共享同一个端口 . 所以问题 .

我在这里先向您的帮助表示感谢!

4 回答

  • 23

    server 套接字侦听单个端口 . 该服务器上所有已 Build 的客户端连接都与该连接的相同侦听端口 on the server side 相关联 . Build 的连接由客户端和服务器端IP /端口对的组合唯一标识 . 只要它们与不同的 client-side IP /端口对相关联,同一服务器上的多个连接可以共享相同的 server-side IP /端口对,并且 server 将能够处理与可用系统资源允许的客户端数量相同的客户端 .

    client-side 上,新的出站连接通常会使用随机 client-side 端口,在这种情况下,如果您在很短的时间内 Build 了大量连接,则可能会耗尽可用端口 .

  • 4

    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,这是“侦听所有IP地址”的代码 . 一个容易犯的错误是只侦听端口127.0.0.1,它只接受来自当前计算机的连接 . 所以这不是连接,这只是意味着请求绑定()到端口IP的进程,并且该进程负责处理到该端口的所有连接 . 这暗示了每个计算机只能在一个端口上监听一个进程的限制(有多种方法可以使用多路复用来解决这个问题,但这是一个更复杂的主题) . 如果Web服务器正在侦听端口80,则它无法与其他Web服务器共享该端口 .

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

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

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

  • 103

    已连接的套接字分配给新的(专用)端口

    这是一种常见的直觉,但这是不正确的 . 连接的套接字未分配给新的/专用端口 . TCP堆栈必须满足的唯一实际约束是元组对于每个套接字连接,(local_address,local_port,remote_address,remote_port)必须是唯一的 . 因此,只要端口上的每个套接字连接到不同的远程位置,服务器就可以使用相同的本地端口具有许多TCP套接字 .

    请参阅"Socket Pair"段落:http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20tuple&pg=PA52#v=onepage&q=socket%20pair%20tuple&f=false

  • 88

    从理论上讲,是的 . 实践,而不是 . 大多数内核(包括linux)都不允许你第二次 bind() 到已经分配的端口 . 这不是一个非常大的补丁,允许这样做 .

    在概念上,我们应该区分套接字和端口 . 套接字是双向通信 endpoints ,即"things",我们可以在其中发送和接收字节 . 这是一个概念性的事情,在名为"socket"的数据包 Headers 中没有这样的字段 .

    端口是能够识别套接字的标识符 . 在TCP的情况下,端口是16位整数,但也有其他协议(例如,在unix套接字上,“端口”本质上是一个字符串) .

    主要问题如下:如果传入的数据包到达,内核可以通过其目标端口号识别其套接字 . 这是一种最常见的方式,但它不是唯一的可能性:

    • 套接字可以通过传入数据包的目标IP进行标识 . 例如,如果我们有一台同时使用两个IP的服务器就是这种情况 . 然后我们可以在相同的端口上运行不同的Web服务器,但是在不同的IP上运行 .

    • 套接字也可以通过它们的源端口和ip来识别 . 在许多负载 balancer 配置中就是这种情况 .

    因为您正在使用应用程序服务器,所以它可以执行此操作 .

相关问题