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