📄 socket.c
字号:
sock = *socketp; REQUIRE(VALID_SOCKET(sock)); LOCK(&sock->lock); REQUIRE(sock->references > 0); sock->references--; if (sock->references == 0) kill_socket = ISC_TRUE; UNLOCK(&sock->lock); if (kill_socket) destroy(&sock); *socketp = NULL;}/* * I/O is possible on a given socket. Schedule an event to this task that * will call an internal function to do the I/O. This will charge the * task with the I/O operation and let our select loop handler get back * to doing something real as fast as possible. * * The socket and manager must be locked before calling this function. */static voiddispatch_recv(isc_socket_t *sock) { intev_t *iev; isc_socketevent_t *ev; INSIST(!sock->pending_recv); ev = ISC_LIST_HEAD(sock->recv_list); if (ev == NULL) return; sock->pending_recv = 1; iev = &sock->readable_ev; socket_log(sock, NULL, EVENT, NULL, 0, 0, "dispatch_recv: event %p -> task %p", ev, ev->ev_sender); sock->references++; iev->ev_sender = sock; iev->ev_action = internal_recv; iev->ev_arg = sock; isc_task_send(ev->ev_sender, (isc_event_t **)&iev);}static voiddispatch_send(isc_socket_t *sock) { intev_t *iev; isc_socketevent_t *ev; INSIST(!sock->pending_send); ev = ISC_LIST_HEAD(sock->send_list); if (ev == NULL) return; sock->pending_send = 1; iev = &sock->writable_ev; socket_log(sock, NULL, EVENT, NULL, 0, 0, "dispatch_send: event %p -> task %p", ev, ev->ev_sender); sock->references++; iev->ev_sender = sock; iev->ev_action = internal_send; iev->ev_arg = sock; isc_task_send(ev->ev_sender, (isc_event_t **)&iev);}/* * Dispatch an internal accept event. */static voiddispatch_accept(isc_socket_t *sock) { intev_t *iev; isc_socket_newconnev_t *ev; INSIST(!sock->pending_accept); /* * Are there any done events left, or were they all canceled * before the manager got the socket lock? */ ev = ISC_LIST_HEAD(sock->accept_list); if (ev == NULL) return; sock->pending_accept = 1; iev = &sock->readable_ev; sock->references++; /* keep socket around for this internal event */ iev->ev_sender = sock; iev->ev_action = internal_accept; iev->ev_arg = sock; isc_task_send(ev->ev_sender, (isc_event_t **)&iev);}static voiddispatch_connect(isc_socket_t *sock) { intev_t *iev; isc_socket_connev_t *ev; iev = &sock->writable_ev; ev = sock->connect_ev; INSIST(ev != NULL); /* XXX */ INSIST(sock->connecting); sock->references++; /* keep socket around for this internal event */ iev->ev_sender = sock; iev->ev_action = internal_connect; iev->ev_arg = sock; isc_task_send(ev->ev_sender, (isc_event_t **)&iev);}/* * Dequeue an item off the given socket's read queue, set the result code * in the done event to the one provided, and send it to the task it was * destined for. * * If the event to be sent is on a list, remove it before sending. If * asked to, send and detach from the socket as well. * * Caller must have the socket locked if the event is attached to the socket. */static voidsend_recvdone_event(isc_socket_t *sock, isc_socketevent_t **dev) { isc_task_t *task; task = (*dev)->ev_sender; (*dev)->ev_sender = sock; if (ISC_LINK_LINKED(*dev, ev_link)) ISC_LIST_DEQUEUE(sock->recv_list, *dev, ev_link); if (((*dev)->attributes & ISC_SOCKEVENTATTR_ATTACHED) == ISC_SOCKEVENTATTR_ATTACHED) isc_task_sendanddetach(&task, (isc_event_t **)dev); else isc_task_send(task, (isc_event_t **)dev);}/* * See comments for send_recvdone_event() above. * * Caller must have the socket locked if the event is attached to the socket. */static voidsend_senddone_event(isc_socket_t *sock, isc_socketevent_t **dev) { isc_task_t *task; INSIST(dev != NULL && *dev != NULL); task = (*dev)->ev_sender; (*dev)->ev_sender = sock; if (ISC_LINK_LINKED(*dev, ev_link)) ISC_LIST_DEQUEUE(sock->send_list, *dev, ev_link); if (((*dev)->attributes & ISC_SOCKEVENTATTR_ATTACHED) == ISC_SOCKEVENTATTR_ATTACHED) isc_task_sendanddetach(&task, (isc_event_t **)dev); else isc_task_send(task, (isc_event_t **)dev);}/* * Call accept() on a socket, to get the new file descriptor. The listen * socket is used as a prototype to create a new isc_socket_t. The new * socket has one outstanding reference. The task receiving the event * will be detached from just after the event is delivered. * * On entry to this function, the event delivered is the internal * readable event, and the first item on the accept_list should be * the done event we want to send. If the list is empty, this is a no-op, * so just unlock and return. */static voidinternal_accept(isc_task_t *me, isc_event_t *ev) { isc_socket_t *sock; isc_socketmgr_t *manager; isc_socket_newconnev_t *dev; isc_task_t *task; ISC_SOCKADDR_LEN_T addrlen; int fd; isc_result_t result = ISC_R_SUCCESS; char strbuf[ISC_STRERRORSIZE]; UNUSED(me); sock = ev->ev_sender; INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); socket_log(sock, NULL, TRACE, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, "internal_accept called, locked socket"); manager = sock->manager; INSIST(VALID_MANAGER(manager)); INSIST(sock->listener); INSIST(sock->pending_accept == 1); sock->pending_accept = 0; INSIST(sock->references > 0); sock->references--; /* the internal event is done with this socket */ if (sock->references == 0) { UNLOCK(&sock->lock); destroy(&sock); return; } /* * Get the first item off the accept list. * If it is empty, unlock the socket and return. */ dev = ISC_LIST_HEAD(sock->accept_list); if (dev == NULL) { UNLOCK(&sock->lock); return; } /* * Try to accept the new connection. If the accept fails with * EAGAIN or EINTR, simply poke the watcher to watch this socket * again. Also ignore ECONNRESET, which has been reported to * be spuriously returned on Linux 2.2.19 although it is not * a documented error for accept(). ECONNABORTED has been * reported for Solaris 8. The rest are thrown in not because * we have seen them but because they are ignored by other * deamons such as BIND 8 and Apache. */ addrlen = sizeof(dev->newsocket->address.type); memset(&dev->newsocket->address.type.sa, 0, addrlen); fd = accept(sock->fd, &dev->newsocket->address.type.sa, (void *)&addrlen);#ifdef F_DUPFD /* * Leave a space for stdio to work in. */ if (fd >= 0 && fd < 20) { int new, tmp; new = fcntl(fd, F_DUPFD, 20); tmp = errno; (void)close(fd); errno = tmp; fd = new; }#endif if (fd < 0) { if (SOFT_ERROR(errno)) goto soft_error; switch (errno) { case ENOBUFS: case ENFILE: case ENOMEM: case ECONNRESET: case ECONNABORTED: case EHOSTUNREACH: case EHOSTDOWN: case ENETUNREACH: case ENETDOWN: case ECONNREFUSED:#ifdef EPROTO case EPROTO:#endif#ifdef ENONET case ENONET:#endif goto soft_error; default: break; } isc__strerror(errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_accept: accept() %s: %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"), strbuf); fd = -1; result = ISC_R_UNEXPECTED; } else { if (addrlen == 0) { UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_accept(): " "accept() failed to return " "remote address"); (void)close(fd); goto soft_error; } else if (dev->newsocket->address.type.sa.sa_family != sock->pf) { UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_accept(): " "accept() returned peer address " "family %u (expected %u)", dev->newsocket->address. type.sa.sa_family, sock->pf); (void)close(fd); goto soft_error; } else if (fd >= (int)FD_SETSIZE) { isc_log_iwrite(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_TOOMANYFDS, "%s: too many open file descriptors", "accept"); (void)close(fd); goto soft_error; } } if (fd != -1) { dev->newsocket->address.length = addrlen; dev->newsocket->pf = sock->pf; } /* * Pull off the done event. */ ISC_LIST_UNLINK(sock->accept_list, dev, ev_link); /* * Poke watcher if there are more pending accepts. */ if (!ISC_LIST_EMPTY(sock->accept_list)) select_poke(sock->manager, sock->fd, SELECT_POKE_ACCEPT); UNLOCK(&sock->lock); if (fd != -1 && (make_nonblock(fd) != ISC_R_SUCCESS)) { (void)close(fd); fd = -1; result = ISC_R_UNEXPECTED; } /* * -1 means the new socket didn't happen. */ if (fd != -1) { LOCK(&manager->lock); ISC_LIST_APPEND(manager->socklist, dev->newsocket, link); dev->newsocket->fd = fd; dev->newsocket->bound = 1; dev->newsocket->connected = 1; /* * Save away the remote address */ dev->address = dev->newsocket->address; manager->fds[fd] = dev->newsocket; manager->fdstate[fd] = MANAGED; if (manager->maxfd < fd) manager->maxfd = fd; socket_log(sock, &dev->newsocket->address, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTEDCXN, "accepted connection, new socket %p", dev->newsocket); UNLOCK(&manager->lock); } else { dev->newsocket->references--; free_socket(&dev->newsocket); } /* * Fill in the done event details and send it off. */ dev->result = result; task = dev->ev_sender; dev->ev_sender = sock; isc_task_sendanddetach(&task, ISC_EVENT_PTR(&dev)); return; soft_error: select_poke(sock->manager, sock->fd, SELECT_POKE_ACCEPT); UNLOCK(&sock->lock); return;}static voidinternal_recv(isc_task_t *me, isc_event_t *ev) { isc_socketevent_t *dev; isc_socket_t *sock; INSIST(ev->ev_type == ISC_SOCKEVENT_INTR); sock = ev->ev_sender; INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); socket_log(sock, NULL, IOEVENT, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALRECV, "internal_recv: task %p got event %p", me, ev); INSIST(sock->pending_recv == 1); sock->pending_recv = 0; INSIST(sock->references > 0); sock->references--; /* the internal event is done with this socket */ if (sock->references == 0) { UNLOCK(&sock->lock); destroy(&sock); return; } /* * Try to do as much I/O as possible on this socket. There are no * limits here, currently. */ dev = ISC_LIST_HEAD(sock->recv_list); while (dev != NULL) { switch (doio_recv(sock, dev)) { case DOIO_SOFT: goto poke; case DOIO_EOF: /* * read of 0 means the remote end was closed. * Run through the event queue and dispatch all * the events with an EOF result code. */ do { dev->result = ISC_R_EOF; send_recvdone_event(sock, &dev); dev = ISC_LIST_HEAD(sock->recv_list); } while (dev != NULL); goto poke; case DOIO_SUCCESS: case DOIO_HARD: send_recvdone_event(sock, &dev); break; } dev = ISC_LIST_HEAD(sock->recv_list); } poke: if (!ISC_LIST_EMPTY(sock->recv_list)) select_poke(sock->manager, sock->fd, SELECT_POKE_READ); UNLOCK(&sock->lock);}static voidinternal_send(isc_task_t *me, isc_event_t *ev) { isc_socketevent_t *dev; isc_socket_t *sock; INSIST(ev->ev_type == ISC_SOCKEVENT_INTW); /* * Find out what socket this is and lock it. */ sock = (isc_socket_t *)ev->ev_sender; INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); socket_log(sock, NULL, IOEVENT, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALSEND, "internal_send: task %p got event %p", me, ev); INSIST(sock->pending_send == 1); sock->pending_send = 0; INSIST(sock->references > 0); sock->references--; /* the internal event is done with this socket */ if (sock->references == 0) { UNLOCK(&sock->lock); destroy(&sock); return; } /* * Try to do as much I/O as possible on this socket. There are no * limits here, currently. */ dev = ISC_LIST_HEAD(sock->send_list); while (dev != NULL) { switch (doio_send(sock, dev)) { case DOIO_SOFT: goto poke; case DOIO_HARD: case DOIO_SUCCESS: send_senddone_event(sock, &dev); break; } dev = ISC_LIST_HEAD(sock->send_list); } poke: if (!ISC_LIST_EMPTY(sock->send_list)) select_poke(sock->manager, sock->fd, SELECT_POKE_WRITE); UNLOCK(&sock->lock);}static voidprocess_fds(isc_socketmgr_t *manager, int maxfd, fd_set *readfds, fd_set *writefds)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -