​javascript:void(0)​


listen 函数仅供服务器端调用,把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求。

1、应用层——listen 函数

[cpp]​ view plain​​​ ​​ copy​​​ ​​ print​​​​?​

  1. #include <sys/socket.h>
  2. int listen(int sockfd, int backlog);
  3. /*sockfd是bind之后的套接口描述字,第二个参数规定了内核应该为相应套接口排队的最大连接个数*/

2、BSD Socket 层——sock_listen 函数

[cpp]​ view plain​​​ ​​ copy​​​ ​​ print​​​​?​

  1. /*
  2. *  Perform a listen. Basically, we allow the protocol to do anything
  3. *  necessary for a listen, and if that works, we mark the socket as
  4. *  ready for listening.
  5. */
  6. //服务器端监听客户端的连接请求
  7. //fd表示bind后的套接字文件描述符,backlog表示排队的最大连接个数
  8. //listen函数把一个未连接的套接字转换为一个被动套接字,
  9. //指示内核应接受该套接字的连接请求
  10. static int sock_listen(int fd, int backlog)
  11. {
  12. struct socket *sock;
  13. if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
  14. return(-EBADF);
  15. //给定文件描述符返回socket结构以及file结构指针,这里file参数为NULL,则表明对这个不感兴趣
  16. if (!(sock = sockfd_lookup(fd, NULL)))
  17. return(-ENOTSOCK);
  18. //前提是没有建立连接,该套接字已经是连接状态了,自然不需要
  19. if (sock->state != SS_UNCONNECTED)
  20. {
  21. return(-EINVAL);
  22. }
  23. //调用底层实现函数(inet_listen)
  24. if (sock->ops && sock->ops->listen)
  25. sock->ops->listen(sock, backlog);
  26. sock->flags |= SO_ACCEPTCON;//设置标识字段,表示正在监听
  27. return(0);
  28. }

3、INET Socket 层——inet_listen 函数

[cpp]​ view plain​​​ ​​ copy​​​ ​​ print​​​​?​

  1. /*
  2. *  Move a socket into listening state.
  3. */
  4. //sock_listen的下层调用函数
  5. //这个函数主要是对sock结构中state字段的设置。listen函数到这层完成处理
  6. static int inet_listen(struct socket *sock, int backlog)
  7. {
  8. //获取sock数据结构
  9. struct sock *sk = (struct sock *) sock->data;
  10. //如果sock的端口号为0(未绑定任何端口号),则自动绑定一个本地端口号(新的未使用的最小的端口号)
  11. //如果事先已经绑定了一个端口号,那么这个代码将不会执行
  12. if(inet_autobind(sk)!=0)
  13. return -EAGAIN;
  14. /* We might as well re use these. */
  15. /*
  16. * note that the backlog is "unsigned char", so truncate it
  17. * somewhere. We might as well truncate it to what everybody
  18. * else does..
  19. */
  20. //等待的最大数.内核限制最大连接数是5
  21. if ((unsigned) backlog > 5)
  22. backlog = 5;
  23. sk->max_ack_backlog = backlog;//缓存的最大未应答数据包个数
  24. if (sk->state != TCP_LISTEN)//如果不是listen状态,则置位listen状态
  25. {
  26. sk->ack_backlog = 0;//缓存的未应答数据包个数清0
  27. sk->state = TCP_LISTEN;
  28. }
  29. return(0);
  30. }

可以看出 inet_listen 函数主要就是设置 sock 的状态为TCP_LISTEN。tcp的三次握手以及四次挥手就是基于这样的一些状态。

函数内部有调用 inet_autobind 函数,该函数是为没有绑定端口的sock结构自动绑定一个端口号(系统可用的最小端口号)

[cpp]​ view plain​​​ ​​ copy​​​ ​​ print​​​​?​

  1. /*
  2. *  Automatically bind an unbound socket.
  3. */
  4. //自动绑定一个本地端口号,一般用于客户端,实际应用层编程时,对于客户端我们并没有
  5. //特别去绑定某个端口号,而是由系统自动绑定
  6. static int inet_autobind(struct sock *sk)
  7. {
  8. /* We may need to bind the socket. */
  9. if (sk->num == 0)
  10. {
  11. //获取一个新的未使用的端口号
  12. sk->num = get_new_socknum(sk->prot, 0);
  13. if (sk->num == 0)
  14. return(-EAGAIN);
  15. put_sock(sk->num, sk);//加入到sock_array哈希表中
  16. //将一个无符号短整型数从网络字节顺序转换为主机字节顺序。大小端问题
  17. sk->dummy_th.source = ntohs(sk->num);//TCP首部中的source字段表示本地端口号
  18. }
  19. return 0;
  20. }

listen 函数把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求,其内部实现归根到底就是设置 sock 结构的状态,设置其为 TCP_LISTEN。功能很简单。

这个函数也是服务器端调用,其套接字的地址信息状态和bind函数执行之后是一样的,只绑定了本地地址信息,不知道对端的地址信息。