listen 函数仅供服务器端调用,把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求。
1、应用层——listen 函数
#include <sys/socket.h> int listen(int sockfd, int backlog); /*sockfd是bind之后的套接口描述字,第二个参数规定了内核应该为相应套接口排队的最大连接个数*/2、BSD Socket 层——sock_listen 函数
/* * Perform a listen. Basically, we allow the protocol to do anything * necessary for a listen, and if that works, we mark the socket as * ready for listening. */ //服务器端监听客户端的连接请求 //fd表示bind后的套接字文件描述符,backlog表示排队的最大连接个数 //listen函数把一个未连接的套接字转换为一个被动套接字, //指示内核应接受该套接字的连接请求 static int sock_listen(int fd, int backlog) { struct socket *sock; if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL) return(-EBADF); //给定文件描述符返回socket结构以及file结构指针,这里file参数为NULL,则表明对这个不感兴趣 if (!(sock = sockfd_lookup(fd, NULL))) return(-ENOTSOCK); //前提是没有建立连接,该套接字已经是连接状态了,自然不需要 if (sock->state != SS_UNCONNECTED) { return(-EINVAL); } //调用底层实现函数(inet_listen) if (sock->ops && sock->ops->listen) sock->ops->listen(sock, backlog); sock->flags |= SO_ACCEPTCON;//设置标识字段,表示正在监听 return(0); }3、INET Socket 层——inet_listen 函数
/* * Move a socket into listening state. */ //sock_listen的下层调用函数 //这个函数主要是对sock结构中state字段的设置。listen函数到这层完成处理 static int inet_listen(struct socket *sock, int backlog) { //获取sock数据结构 struct sock *sk = (struct sock *) sock->data; //如果sock的端口号为0(未绑定任何端口号),则自动绑定一个本地端口号(新的未使用的最小的端口号) //如果事先已经绑定了一个端口号,那么这个代码将不会执行 if(inet_autobind(sk)!=0) return -EAGAIN; /* We might as well re use these. */ /* * note that the backlog is "unsigned char", so truncate it * somewhere. We might as well truncate it to what everybody * else does.. */ //等待的最大数.内核限制最大连接数是5 if ((unsigned) backlog > 5) backlog = 5; sk->max_ack_backlog = backlog;//缓存的最大未应答数据包个数 if (sk->state != TCP_LISTEN)//如果不是listen状态,则置位listen状态 { sk->ack_backlog = 0;//缓存的未应答数据包个数清0 sk->state = TCP_LISTEN; } return(0); }可以看出 inet_listen 函数主要就是设置 sock 的状态为TCP_LISTEN。tcp的三次握手以及四次挥手就是基于这样的一些状态。
函数内部有调用 inet_autobind 函数,该函数是为没有绑定端口的sock结构自动绑定一个端口号(系统可用的最小端口号)
/* * Automatically bind an unbound socket. */ //自动绑定一个本地端口号,一般用于客户端,实际应用层编程时,对于客户端我们并没有 //特别去绑定某个端口号,而是由系统自动绑定 static int inet_autobind(struct sock *sk) { /* We may need to bind the socket. */ if (sk->num == 0) { //获取一个新的未使用的端口号 sk->num = get_new_socknum(sk->prot, 0); if (sk->num == 0) return(-EAGAIN); put_sock(sk->num, sk);//加入到sock_array哈希表中 //将一个无符号短整型数从网络字节顺序转换为主机字节顺序。大小端问题 sk->dummy_th.source = ntohs(sk->num);//TCP首部中的source字段表示本地端口号 } return 0; }listen 函数把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求,其内部实现归根到底就是设置 sock 结构的状态,设置其为 TCP_LISTEN。功能很简单。