为何accept返回的socket与传入参数socket不同?连接传递机制疑问
关于TCP
accept() 的两个核心疑问解析 首先先看accept()的函数原型:
int accept(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);
用户疑问
- 疑问1:
accept方法接收一个监听fd(socket)并返回一个连接fd(conn fd),二者必然不同,但它们使用相同端口,为何仍有区别? - 疑问2:
listen方法负责监听连接,当TCP三次握手完成后,socket变为可接受状态,那么在accept过程中,监听socket是如何将TCP连接传递给新的连接fd对应的socket的?
解答
针对疑问1:同端口但fd不同的本质原因
其实这两个socket的职责和状态完全不同:
- 监听fd对应的socket处于
LISTEN状态,它的唯一作用就是在绑定的端口上被动等待客户端的连接请求,本身不会和任何客户端进行实际的数据交互。它就像公司的前台,只负责接待访客、登记信息,不处理具体业务。 - 连接fd对应的socket是内核专门为已完成三次握手的特定客户端创建的,它的四元组(源IP、源端口、目的IP、目的端口)是唯一的——虽然目的端口和监听socket一致,但源端的客户端信息是独有的,内核正是靠这个四元组来精准区分不同的连接,把数据投递到对应的连接fd上。这个socket就像专属业务员,只对接自己负责的那个访客,处理具体的业务往来。
简单说:同端口只是因为它们都属于同一台服务器的同一个服务入口,但一个负责"接客",一个负责"待客",内核用不同的fd来区分这两种完全不同的socket角色。
针对疑问2:accept传递连接的底层逻辑
内核在你调用listen()时,就会为监听socket维护两个关键队列:
- 半连接队列(SYN队列):存放正在进行三次握手的连接——客户端发了SYN,服务器回复了SYN+ACK,但还没收到客户端的ACK时,连接就存在这里。
- 全连接队列(ACCEPT队列):存放已经完成三次握手、等待应用层调用
accept()来处理的连接。
当客户端完成第三次握手(发送ACK)后,对应的连接会从半连接队列转移到全连接队列,进入"待处理"状态。此时当你调用accept():
- 内核会从全连接队列的头部取出第一个等待的连接;
- 然后创建一个全新的socket(对应返回的连接fd),这个socket会继承监听socket的端口、协议等属性,但会直接绑定这个已经建立好的唯一四元组;
- 最后把这个新socket的fd返回给应用层,监听socket则继续留在原地,等待新的连接请求。
这个过程就像前台把登记完成的访客领到专属业务员面前,之后前台继续接待新访客,业务员则和这个访客开展具体业务。
内容的提问来源于stack exchange,提问作者wwulfric
相关产品推荐
相关产品推荐

