Listener fd的创建以及监听代码位于[src/proto_tcp.c]的tcp_bind_listener函数中。
- [src/proto_tcp.c] tcp_bind_listener()
- int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
- {
- __label__ tcp_return, tcp_close_return;
- int fd, err;
- const char *msg = NULL;
-
- /* ensure we never return garbage */
- if (errmsg && errlen)
- *errmsg = 0;
-
- if (listener->state != LI_ASSIGNED)
- return ERR_NONE; /* already bound */
为什么对于listener->state != LI_ASSIGNED的情况下,它就是绑定的了呢?那么看下listener入队时候是怎么操作的。
- [src/proto_tcp.c] tcpv4_add_listener()
- /* Add listener to the list of tcpv4 listeners. The listener's state
- * is automatically updated from LI_INIT to LI_ASSIGNED. The number of
- * listeners is updated. This is the function to use to add a new listener.
- */
- void tcpv4_add_listener(struct listener *listener)
- {
- if (listener->state != LI_INIT)
- return;
- listener->state = LI_ASSIGNED;
- listener->proto = &proto_tcpv4;
- LIST_ADDQ(&proto_tcpv4.listeners, &listener->proto_list);
- proto_tcpv4.nb_listeners++;
- }
很明显,listener在入队时状态会自动改变成LI_ASSIGNED,因此对于处于队列中的listener,他们的状态至少为LI_ASSIGNED,也就是说,如果不为ASSIGNED,那么必定已经绑定过来了。对于已经绑定然后解绑定的listener,只要没有移出队列,那么其状态也是LI_ASSIGNED。如果从队列中移除,那么状态就要小于LI_ASSIGNED了,从添加的过程要求状态为LI_INIT,大致可以知道,其从队列删除时,状态会变成LI_INIT,事实上也是如此。
- [src/proto_tcp.c] tcp_bind_listener()
- err = ERR_NONE;
-
- if ((fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- err |= ERR_RETRYABLE | ERR_ALERT;
- msg = "cannot create listening socket";
- goto tcp_return;
- }
-
- if (fd >= global.maxsock) {
- err |= ERR_FATAL | ERR_ABORT | ERR_ALERT;
- msg = "not enough free sockets (raise '-n' parameter)";
- goto tcp_close_return;
- }
-
- if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
- err |= ERR_FATAL | ERR_ALERT;
- msg = "cannot make socket non-blocking";
- goto tcp_close_return;
- }
创建socket,然后判断数量是否超越最大值(作者很多地方都习惯于先分配再判断,这应该是与其维护内核代码相关的)。如果没有超出限制,那么将fd设置为非阻塞。
- [src/proto_tcp.c] tcp_bind_listener()
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
- /* not fatal but should be reported */
- msg = "cannot do so_reuseaddr";
- err |= ERR_ALERT;
- }
-
- if (listener->options & LI_O_NOLINGER)
- setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger));
-
- #ifdef SO_REUSEPORT
- /* OpenBSD supports this. As it's present in old libc versions of Linux,
- * it might return an error that we will silently ignore.
- */
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one));
- #endif
- #ifdef CONFIG_HAP_LINUX_TPROXY
- if ((listener->options & LI_O_FOREIGN)
- && (setsockopt(fd, SOL_IP, IP_TRANSPARENT, (char *) &one, sizeof(one)) == -1)
- && (setsockopt(fd, SOL_IP, IP_FREEBIND, (char *) &one, sizeof(one)) == -1)) {
- msg = "cannot make listening socket transparent";
- err |= ERR_ALERT;
- }
- #endif
- #ifdef SO_BINDTODEVICE
- /* Note: this might fail if not CAP_NET_RAW */
- if (listener->interface) {
- if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
- listener->interface, strlen(listener->interface) + 1) == -1) {
- msg = "cannot bind listener to device";
- err |= ERR_WARN;
- }
- }
- #endif
- #if defined(TCP_MAXSEG)
- if (listener->maxseg) {
- if (setsockopt(fd, IPPROTO_TCP, TCP_MAXSEG,
- &listener->maxseg, sizeof(listener->maxseg)) == -1) {
- msg = "cannot set MSS";
- err |= ERR_WARN;
- }
- }
- #endif
- #if defined(TCP_DEFER_ACCEPT)
- if (listener->options & LI_O_DEF_ACCEPT) {
- /* defer accept by up to one second */
- int accept_delay = 1;
- if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &accept_delay, sizeof(accept_delay)) == -1) {
- msg = "cannot enable DEFER_ACCEPT";
- err |= ERR_WARN;
- }
- }
- #endif
根据配置,设置socket的选项。其中要说明的是TCP_DEFER_ACCEPT,这个选项要求在backlog中的连接,需要有数据才会被应用程序接收。
- [src/proto_tcp.c] tcp_bind_listener()
- if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {
- err |= ERR_RETRYABLE | ERR_ALERT;
- msg = "cannot bind socket";
- goto tcp_close_return;
- }
-
- if (listen(fd, listener->backlog ? listener->backlog : listener->maxconn) == -1) {
- err |= ERR_RETRYABLE | ERR_ALERT;
- msg = "cannot listen to socket";
- goto tcp_close_return;
- }
-
- #if defined(TCP_QUICKACK)
- if (listener->options & LI_O_NOQUICKACK)
- setsockopt(fd, IPPROTO_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero));
- #endif
-
- /* the socket is ready */
- listener->fd = fd;
- listener->state = LI_LISTEN;
绑定并监听,而且根据配置可选地关闭掉TCP_QUICKACK。此时listener的状态也要相应的调整为LI_LISTEN。
- [src/proto_tcp.c] tcp_bind_listener()
- /* the function for the accept() event */
- fd_insert(fd);
- fdtab[fd].cb[DIR_RD].f = listener->accept;
- fdtab[fd].cb[DIR_WR].f = NULL; /* never called */
- fdtab[fd].cb[DIR_RD].b = fdtab[fd].cb[DIR_WR].b = NULL;
- fdtab[fd].owner = listener; /* reference the listener instead of a task */
- fdtab[fd].state = FD_STLISTEN;
- fdtab[fd].flags = FD_FL_TCP;
- if (listener->options & LI_O_NOLINGER)
- fdtab[fd].flags |= FD_FL_TCP_NOLING;
-
- fdinfo[fd].peeraddr = NULL;
- fdinfo[fd].peerlen = 0;
将fd插入fdtab中。并设置一些状态,以及回调函数。从此处可以看出,监听描述符对应的read函数来自于listener的accept成员,write函数为空,对于fdtab中的listner fd来说,他们的owner为对应的listner。Listner的accept成员被赋予event_accept函数,这是在解析配置文件的过程中进行的。待以后讲解main函数的时候再讲解。对于listener要求设置NOLINGER选项的,还需要加上程序本身的NOLINGER选项。
- [proto/fd.h] fd_insert()
- static inline void fd_insert(int fd)
- {
- fdtab[fd].ev = 0;
- if (fd + 1 > maxfd)
- maxfd = fd + 1;
- }
Fd_insert的操作非常简单。将从poll中返回已处理事件置空,然后更新maxfd。
- [src/proto_tcp.c] tcp_bind_listener()
- tcp_return:
- if (msg && errlen)
- strlcpy2(errmsg, msg, errlen);
- return err;
-
- tcp_close_return:
- close(fd);
- goto tcp_return;
- }
此处Listener fd的操作与常见的操作不一样的只有最后对于fdtab的处理。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。