它作两件事:html
一、当socket函数建立一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起链接的客户套接字。listen函数把一个未链接的套接字转换为一个被动套接字,指示内核应该接受指向该套接字的链接请求。根据TCP状态转换图,调用listen致使套接字从CLOSED状态转换到LISTEN状态。服务器
二、listen函数的第二个参数规定了内核应该为相应套接字排队的最大链接个数:网络
1 #include<sys/socket.h> 2 int listen(int sockfd, int backlog); 3 返回:若成功则为0,若出错则为-1
为了理解其中的backlog参数,咱们必须认识到内核为任何一个给定的监听套接字维护两个队列:数据结构
(1)未完成链接队列,每一个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程。这些套接字处于SYN_RECV状态socket
(2)已完成链接队列,每一个已完成TCP三路握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。tcp
下图描绘了监听套接字的两个队列函数
每当在未完成链接队列中建立一项时,来自监听套接字的参数就复制到即将创建的链接中,链接的建立机制是彻底自动的。无需服务器进程插手。下图展现了用这两个队列创建链接时所交换的分组:学习
当来自客户的SYN到达时,TCP在未完成链接队列中建立一个新项,而后响应以三路握手的第二个分节:服务器的SYN响应,其中捎带对客户SYN的ACK。这一项一直保留在未完成链接队列中,直到三路握手的第三个分节(客户对服务器的SYN的ACK)到达或者该项超时为止。spa
若是三路握手正常完成,该项从未完成链接队列移到已完成链接队列的队尾。当进程调用accept时,已完成链接队列中的队头项将返回给进程,或者该队列为空,那么进程就被投入睡眠,直到TCP在该队列中放入一项才唤醒它。code
一、accept()函数不参与三次握手,而只负责从创建链接队列中取出一个链接和socketfd进行绑定;
二、backlog参数决定了未完成队列和已完成队列中链接数目之和的最大值(从内核的角度,是否这个和就是等于sock->recv_queue ?);
三、accept()函数调用,会从已链接队列中取出一个“链接”(能够说描述的数据结构listensocket->sock->recv_queue[sk_buff] ? ),未完成队列和已完成队列中链接目之和将减小1;即accept将监听套接字对应的sock的接收队列中的已创建链接的sk_buff取下(从该sk_buff中能够得到对端主机的发送过来的tcp/ip数据包)
四、 监听套接字的已完成队列中的元素个数大于0,那么该套接字是可读的。
五、 当程序调用accept的时候(设置阻塞参数),那么断定该套接字是否可读,不可读则进入睡眠,直至已完成队列中的元素个数大于0(监听套接字可读)而唤起监听进程)
本文仅用于我的学习。
Ref:
http://www.cnblogs.com/chris-cp/p/4022262.html
http://www.cnblogs.com/lengender-12/archive/2017/05/05/6813057.html