socket API的accept()函数是如何工作的?

套接字API是TCP / IP和UDP / IP通信的实际标准(也就是我们所知道的networking代码)。 然而,其核心function之一accept()有点神奇。

借用一个半正式的定义:

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

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

accept工作如何? 它是如何实现的? 这个话题有很多困惑。 许多人声称接受打开一个新的港口,你通过它与客户沟通。 但这显然是不正确的,因为没有新的港口开放。 你实际上可以通过同一个端口与不同的客户进行通信,但是怎么样? 当几个线程在同一个端口上调用recv时,数据如何知道去哪里?

我想这是沿着与套接字描述符相关联的客户端地址的行,并且每当数据通过recv来传递它的路由到正确的套接字,但我不知道。

对这个机制的内部工作进行彻底的解释会很棒。

您的困惑在于认为套接字由服务器IP:服务器端口标识。 实际上,套接字是由四个信息唯一标识的:

Client IP : Client PortServer IP : Server Port

因此,尽pipe服务器IP和服务器端口在所有接受的连接中都是不变的,但是客户端信息可以让它跟踪所有事情的进展。

举例说明事情:

假设我们有192.168.1.1:80的服务器和两个客户端10.0.0.1和10.0.0.2。

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

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

只是为了补充用户给出的答案“26的26”

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

因此,服务器上的不同应用程序可能需要在同一个四元组上进行同一个客户端的通信,但协议域不同。 例如

在服务器端的Apache(TCP上的server1.com:880-client1:1234)和魔兽世界的聊天(UDP上的server1.com:880-client1:1234)

无论是客户端还是服务器,都会将这个IP数据包中的协议字段作为处理对象,即使其他4个字段都是相同的。

当我了解到这一点时,我感到困惑的是,术语socketport表明它们是物理的,实际上它们只是内核用来抽象networking细节的数据结构。

因此,数据结构被实现为能够将来自不同客户端的连接分开。 至于它们是如何实现的,答案或者是a)。没关系,socket API的目的恰恰就是实现不重要,或者b)只是看看。 除了高度推荐的Stevens书籍提供了一个实现的详细描述之外,请查看Linux或Solaris或其中一个BSD的源代码。

正如另一个人所说,一个套接字由一个4元组(客户端IP,客户端口,服务器IP,服务器端口)唯一标识。

在服务器IP上运行的服务器进程维护一个数据库(意思是我不关心它使用什么样的table / list / tree / array / magic数据结构)活动套接字并在服务器端口上侦听。 当它收到一条消息(通过服务器的TCP / IP堆栈)时,它会根据数据库检查客户端IP和端口。 如果在数据库条目中find客户端IP和客户端端口,则将该消息传递给现有的处理程序,否则会创build一个新的数据库条目并生成一个新的处理程序来处理该套接字。

在ARPAnet的早期,某些协议(用于一个的FTP)将侦听指定的端口用于连接请求,并用切换端口来回复。 进一步的通信连接将通过越区切换端口。 这样做是为了提高每个数据包的性能:在那些日子里,电脑的速度要慢几个数量级。