大家都知道,在创建一个服务器程序的时候,需要先listen一下,然后才能接收客户端的请求。例如下面的这段代码我们再熟悉不过了。int main(int argc, char const *argv[]){ int fd = socket(AF_INET, SOCK_STREAM, 0); bind(fd, ); listen(fd, 128); accept(fd, );
那么我们今天来思考一个问题,为什么需要listen一下才能接收连接?或者换句话说,listen内部执行的时候到底干了啥?
如果你也想搞清楚listen内部的这些秘密,那么请跟我来!一、创建socket
服务器要做的第一件事就是先创建一个socket。具体就是通过调用socket函数。当socket函数执行完毕后,在用户层视角我们是看到返回了一个文件描述符fd。但在内核中其实是一套内核对象组合,大体结构如下。
这里简单了解这个结构就行,后面我们在源码中看到函数指针调用的时候需要回头再来看它。二、内核执行listen2.1listen系统调用
我在net/socket.c下找到了listen系统调用的源码。//file: net/socket.cSYSCALL_DEFINE2(listen, int, fd, int, backlog){ //根据 fd 查找 socket 内核对象 sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) {//获取内核参数 net.core.somaxconnsomaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;if ((unsigned int)backlog > somaxconn)backlog = somaxconn;//调用协议栈注册的 listen 函数err = sock->ops->listen(sock, backlog);}
用户态的socket文件描述符只是一个整数而已,内核是没有办法直接用的。所以该函数中第一行代码就是根据用户传入的文件描述符来查找到对应的socket内核对象。
再接着获取了系统里的net.core.somaxconn内核参数的值,和用户传入的backlog比较后取一个最小值传入到下一步中。