套接字API accept()函数如何工作?


126

套接字API是用于TCP / IP和UDP / IP通信的事实标准(即我们所知道的网络代码)。但是,它的核心功能之一accept()是不可思议的。

借用半正式的定义:

在服务器端使用accept()。它接受从远程客户端创建新的TCP连接的传入尝试,并创建与此连接的套接字地址对关联的新套接字。

换句话说,accept返回一个新的套接字,服务器可以通过该套接字与新连接的客户端进行通信。旧的套接字(在其上accept被调用)保持打开状态,在同一端口上,侦听新的连接。

accept工作如何?如何实施?在这个话题上有很多困惑。许多人声称接受会打开一个新端口,而您通过它与客户端进行通信。但这显然是不正确的,因为没有打开任何新端口。实际上,您可以通过同一端口与不同的客户端进行通信,但是如何进行呢?当多个线程recv在同一端口上调用时,数据如何知道去向?

我想这是客户端地址与套接字描述符相关联的东西,每当有数据通过时,recv它都会路由到正确的套接字,但是我不确定。

全面了解此机制的内部工作非常好。


2
因此对于每个客户端请求,服务器端都会打开一个全新的套接字连接。服务器必须始终在80打开,以监听来电。如果收到呼叫,它将立即使用下面提到的四个元组创建一个NEW套接字,这将在客户端和服务器之间建立TCP连接。我的理解正确吗?
头脑风暴

1
这是一个非常基本的问题,我最近在一次采访中对此进行了测试:stackoverflow.com/questions/24871827/…如果对此有任何评论,请发布
头脑风暴

@brainstorm仅当您完全忽略HTTP保持活动的存在时。
罗恩侯爵

Answers:


140

您的困惑在于认为套接字由Server IP标识:Server Port。实际上,套接字是由四重信息唯一标识的:

Client IP : Client PortServer IP : Server Port

因此,尽管服务器IP和服务器端口在所有接受的连接中都是恒定的,但客户端信息才是使它能够跟踪一切进展的原因。

澄清事物的例子:

假设我们有一个服务器,192.168.1.1:80并且有两个客户端,10.0.0.1并且10.0.0.2

10.0.0.1打开本地端口上的连接1234并连接到服务器。现在,服务器具有一个标识如下的套接字:

10.0.0.1:1234 - 192.168.1.1:80  

现在10.0.0.2在本地端口上打开一个连接5678并连接到服务器。现在,服务器具有两个插槽,标识如下:

10.0.0.1:1234 - 192.168.1.1:80  
10.0.0.2:5678 - 192.168.1.1:80

3
我不知道实现细节(可能因平台而异),我只知道套接字在概念上是由我描述的四重信息标识的。

3
您对此有参考吗?
2010年

3
随机问题:如果使用NAT,并且连接到服务器时同一网络上的两个客户端尝试使用相同的本地端口,会发生什么情况?例如,如果10.0.0.1和10.0.0.2都连接到外部IP为192.168.0.1的路由器,则位于192.168.1.1的服务器将看到来自192.168.0.1的两个连接。在这种情况下,如果10.0.0.1和10.0.0.2都通过随机数生成器选择了相同的本地端口,会发生什么情况?
aroth's

4
路由器中的NAT支持负责那里的细节。网络流量实际上是通过两个连接进行的-客户端到路由器,以及路由器到服务器。路由器在两个不同的端口192.168.0.1:1234和192.168.0.1:5678上建立传出连接。然后,传入流量由路由器重定向到正确的客户端。

3
如果套接字由四方标识,那么侦听套接字的四方信息是什么?
Eric Zheng

74

仅用于添加用户“ 26之17”给出的答案

套接字实际上由5个元组组成-(源IP,源端口,目标IP,目标端口,协议)。这里的协议可以是TCP或UDP或任何传输层协议。该协议在IP数据报中的“协议”字段中的数据包中进行标识。

因此,可能必须使服务器上的不同应用程序通过完全相同的4元组但在协议字段上与相同的客户端通信。例如

服务器端的Apache通话(TCP上的server1.com:880-client1:1234)和《魔兽世界》通话(UDP上的server1.com:880-client1:1234)

客户端和服务器都将处理此问题,因为在这两种情况下,即使其他四个字段都相同,IP数据包中的协议字段也不相同。


13

当我学习这些时,令我感到困惑的是这些术语socketport暗示它们是物理的,而实际上它们只是内核用来抽象化网络细节的数据结构。

这样,数据结构被实现为能够使来自不同客户端的连接保持分开。至于如何他们正在实施,答案可以是一个。)也没关系,套接字API的目的恰恰是实施不应该的问题或b。)只要看看。除了极力推荐的Stevens书籍,其中详细介绍了一种实现,还请查看Linux或Solaris或BSD之一的源代码。


是的,大多数网络术语只是将名称分配给某些位集合,并根据其值(“协议标识符”,“路由”,“绑定”,“套接字”等)做出决定。您所有网卡的硬件都旨在接收比特流。与计算机上的程序相关的事件由驱动程序和操作系统决定。如果需要的话,明天我们可以摆脱所有这些术语,但是传递比特流的原理似乎至关重要……
masterxilo

-1

就像另一个人说的那样,套接字由一个四元组(客户端IP,客户端端口,服务器IP,服务器端口)唯一标识。

在服务器IP上运行的服务器进程维护活动套接字的数据库(这意味着我不在乎它使用哪种表/列表/树/数组/魔术数据结构),并在服务器端口上进行侦听。当它收到一条消息时(通过服务器的TCP / IP堆栈),它将对照数据库检查客户端IP和端口。如果在数据库条目中找到“客户端IP”和“客户端端口”,则将消息移交给现有的处理程序,否则将创建新的数据库条目,并产生一个新的处理程序来处理该套接字。

在ARPAnet成立之初,某些协议(一种用于FTP)会侦听指定端口的连接请求,并使用切换端口进行应答。该连接的进一步通信将通过切换端口。这样做是为了提高每个数据包的性能:当时的计算机速度要慢几个数量级。


您能否详细说明“移交端口”部分?
Eli Bendersky

1
这要么是对某些TCP之前协议的描述,要么是过于简化。尝试连接到侦听套接字的客户端发送特殊数据包以建立连接(SYN位置1)。创建新套接字的数据包与使用现有套接字的数据包之间有明显的区别。
约翰M,2009年

...发送一个特殊的数据包以建立连接(SYN位置1)。哪个(据我了解)导致协议栈将其提供给“ the”侦听器(如果有),这就是为什么每个地址/端口/协议组合只能有一个侦听端口的原因。我不确定这是在规范中还是只是实现约定。
Peter Wone 2013年

1
第二段未正确描述在TCP层或服务器进程中发生的情况。服务器进程不需要维护任何类型的套接字的数据结构,也不需要检查传入的IP:端口对是否有任何异常。那就是套接字的用途。FTP使用单独的端口来存储数据,而不是用于所有“进一步的通信”,并且使用帽子简化协议不是出于性能原因。使用新端口不会以任何方式提高性能。
罗恩侯爵

“维护数据库(这意味着我不在乎它使用哪种表/列表/树/数组/魔术数据结构)” :)我通常将其称为“表”(或“图形”或“决策树”) )。“数据库”向我建议了一些实现。
masterxilo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.