打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
五、listener fd的初始化

Listener fd的创建以及监听代码位于[src/proto_tcp.c]的tcp_bind_listener函数中。

  1. [src/proto_tcp.c] tcp_bind_listener()  
  2. int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)  
  3. {  
  4.     __label__ tcp_return, tcp_close_return;  
  5.     int fd, err;  
  6.     const char *msg = NULL;  
  7.   
  8.     /* ensure we never return garbage */  
  9.     if (errmsg && errlen)  
  10.         *errmsg = 0;  
  11.   
  12.     if (listener->state != LI_ASSIGNED)  
  13.         return ERR_NONE; /* already bound */  

为什么对于listener->state != LI_ASSIGNED的情况下,它就是绑定的了呢?那么看下listener入队时候是怎么操作的。

  1. [src/proto_tcp.c] tcpv4_add_listener()  
  2. /* Add listener to the list of tcpv4 listeners. The listener's state 
  3.  * is automatically updated from LI_INIT to LI_ASSIGNED. The number of 
  4.  * listeners is updated. This is the function to use to add a new listener. 
  5.  */  
  6. void tcpv4_add_listener(struct listener *listener)  
  7. {  
  8.     if (listener->state != LI_INIT)  
  9.         return;  
  10.     listener->state = LI_ASSIGNED;  
  11.     listener->proto = &proto_tcpv4;  
  12.     LIST_ADDQ(&proto_tcpv4.listeners, &listener->proto_list);  
  13.     proto_tcpv4.nb_listeners++;  
  14. }  

很明显,listener在入队时状态会自动改变成LI_ASSIGNED,因此对于处于队列中的listener,他们的状态至少为LI_ASSIGNED,也就是说,如果不为ASSIGNED,那么必定已经绑定过来了。对于已经绑定然后解绑定的listener,只要没有移出队列,那么其状态也是LI_ASSIGNED。如果从队列中移除,那么状态就要小于LI_ASSIGNED了,从添加的过程要求状态为LI_INIT,大致可以知道,其从队列删除时,状态会变成LI_INIT,事实上也是如此。

  1. [src/proto_tcp.c] tcp_bind_listener()  
  2.     err = ERR_NONE;  
  3.   
  4.     if ((fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {  
  5.         err |= ERR_RETRYABLE | ERR_ALERT;  
  6.         msg = "cannot create listening socket";  
  7.         goto tcp_return;  
  8.     }  
  9.   
  10.     if (fd >= global.maxsock) {  
  11.         err |= ERR_FATAL | ERR_ABORT | ERR_ALERT;  
  12.         msg = "not enough free sockets (raise '-n' parameter)";  
  13.         goto tcp_close_return;  
  14.     }  
  15.   
  16.     if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {  
  17.         err |= ERR_FATAL | ERR_ALERT;  
  18.         msg = "cannot make socket non-blocking";  
  19.         goto tcp_close_return;  
  20.     }  

创建socket,然后判断数量是否超越最大值(作者很多地方都习惯于先分配再判断,这应该是与其维护内核代码相关的)。如果没有超出限制,那么将fd设置为非阻塞。

  1. [src/proto_tcp.c] tcp_bind_listener()  
  2.     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {  
  3.         /* not fatal but should be reported */  
  4.         msg = "cannot do so_reuseaddr";  
  5.         err |= ERR_ALERT;  
  6.     }  
  7.   
  8.     if (listener->options & LI_O_NOLINGER)  
  9.         setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));  
  10.   
  11. #ifdef SO_REUSEPORT  
  12.     /* OpenBSD supports this. As it's present in old libc versions of Linux, 
  13.      * it might return an error that we will silently ignore. 
  14.      */  
  15.     setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one));  
  16. #endif  
  17. #ifdef CONFIG_HAP_LINUX_TPROXY  
  18.     if ((listener->options & LI_O_FOREIGN)  
  19.         && (setsockopt(fd, SOL_IP, IP_TRANSPARENT, (char *) &one, sizeof(one)) == -1)  
  20.         && (setsockopt(fd, SOL_IP, IP_FREEBIND, (char *) &one, sizeof(one)) == -1)) {  
  21.         msg = "cannot make listening socket transparent";  
  22.         err |= ERR_ALERT;  
  23.     }  
  24. #endif  
  25. #ifdef SO_BINDTODEVICE  
  26.     /* Note: this might fail if not CAP_NET_RAW */  
  27.     if (listener->interface) {  
  28.         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,  
  29.                    listener->interface, strlen(listener->interface) + 1) == -1) {  
  30.             msg = "cannot bind listener to device";  
  31.             err |= ERR_WARN;  
  32.         }  
  33.     }  
  34. #endif  
  35. #if defined(TCP_MAXSEG)  
  36.     if (listener->maxseg) {  
  37.         if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG,  
  38.                    &listener->maxseg, sizeof(listener->maxseg)) == -1) {  
  39.             msg = "cannot set MSS";  
  40.             err |= ERR_WARN;  
  41.         }  
  42.     }  
  43. #endif  
  44. #if defined(TCP_DEFER_ACCEPT)  
  45.     if (listener->options & LI_O_DEF_ACCEPT) {  
  46.         /* defer accept by up to one second */  
  47.         int accept_delay = 1;  
  48.         if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &accept_delay, sizeof(accept_delay)) == -1) {  
  49.             msg = "cannot enable DEFER_ACCEPT";  
  50.             err |= ERR_WARN;  
  51.         }  
  52.     }  
  53. #endif  

根据配置,设置socket的选项。其中要说明的是TCP_DEFER_ACCEPT,这个选项要求在backlog中的连接,需要有数据才会被应用程序接收。

  1. [src/proto_tcp.c] tcp_bind_listener()  
  2.     if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {  
  3.         err |= ERR_RETRYABLE | ERR_ALERT;  
  4.         msg = "cannot bind socket";  
  5.         goto tcp_close_return;  
  6.     }  
  7.   
  8.     if (listen(fd, listener->backlog ? listener->backlog : listener->maxconn) == -1) {  
  9.         err |= ERR_RETRYABLE | ERR_ALERT;  
  10.         msg = "cannot listen to socket";  
  11.         goto tcp_close_return;  
  12.     }  
  13.   
  14. #if defined(TCP_QUICKACK)  
  15.     if (listener->options & LI_O_NOQUICKACK)  
  16.         setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero));  
  17. #endif  
  18.   
  19.     /* the socket is ready */  
  20.     listener->fd = fd;  
  21.     listener->state = LI_LISTEN;  

绑定并监听,而且根据配置可选地关闭掉TCP_QUICKACK。此时listener的状态也要相应的调整为LI_LISTEN。

  1. [src/proto_tcp.c] tcp_bind_listener()  
  2.     /* the function for the accept() event */  
  3.     fd_insert(fd);  
  4.     fdtab[fd].cb[DIR_RD].f = listener->accept;  
  5.     fdtab[fd].cb[DIR_WR].f = NULL; /* never called */  
  6.     fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL;  
  7.     fdtab[fd].owner = listener; /* reference the listener instead of a task */  
  8.     fdtab[fd].state = FD_STLISTEN;  
  9.     fdtab[fd].flags = FD_FL_TCP;  
  10.     if (listener->options & LI_O_NOLINGER)  
  11.         fdtab[fd].flags |= FD_FL_TCP_NOLING;  
  12.   
  13.     fdinfo[fd].peeraddr = NULL;  
  14.     fdinfo[fd].peerlen = 0;  

将fd插入fdtab中。并设置一些状态,以及回调函数。从此处可以看出,监听描述符对应的read函数来自于listeneraccept成员,write函数为空,对于fdtab中的listner fd来说,他们的owner为对应的listnerListneraccept成员被赋予event_accept函数,这是在解析配置文件的过程中进行的。待以后讲解main函数的时候再讲解。对于listener要求设置NOLINGER选项的,还需要加上程序本身的NOLINGER选项。

  1. [proto/fd.h] fd_insert()  
  2. static inline void fd_insert(int fd)  
  3. {  
  4.     fdtab[fd].ev = 0;  
  5.     if (fd + 1 > maxfd)  
  6.         maxfd = fd + 1;  
  7. }  

Fd_insert的操作非常简单。将从poll中返回已处理事件置空,然后更新maxfd

  1. [src/proto_tcp.c] tcp_bind_listener()  
  2.  tcp_return:  
  3.     if (msg && errlen)  
  4.         strlcpy2(errmsg, msg, errlen);  
  5.     return err;  
  6.   
  7.  tcp_close_return:  
  8.     close(fd);  
  9.     goto tcp_return;  
  10. }  

此处Listener fd的操作与常见的操作不一样的只有最后对于fdtab的处理。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【Go语言实战】(4) 简简单单的几十行代码实现 TCP 通信
TCP/IP编程基础——超时、多路复用、非阻塞
一个解除TCP连接的TIME_WAIT状态限制的简便方法
使用 Go 语言实现优雅的服务器重启
ORA-3136 连接超时问题
ORA-01034和ORA-27101的解决方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服