📄 socket.c
字号:
/* * 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_socket_t *sock, int accept_errno) { isc_socketmgr_t *manager; isc_socket_newconnev_t *dev; isc_task_t *task; ISC_SOCKADDR_LEN_T addrlen; SOCKET fd; isc_result_t result = ISC_R_SUCCESS; char strbuf[ISC_STRERRORSIZE]; 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->hEvent != NULL); INSIST(sock->pending_accept == 1); sock->pending_accept = 0; /* * Check any possible error status from the event notification here. * Note that we don't take any action since it was only * Windows that was notifying about a network event, not the * application. * PDMXXX: Should we care about any of the possible event errors * signalled? The only possible valid errors are: * WSAENETDOWN, WSAECONNRESET, & WSAECONNABORTED */ if (accept_errno != 0) { switch (accept_errno) { case WSAENETDOWN: case WSAECONNRESET: case WSAECONNABORTED: break; /* Expected errors */ default: isc__strerror(accept_errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_accept: from event wait: %s", strbuf); break; } UNLOCK(&sock->lock); 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) { isc_sockaddr_t from; /* * This should only happen if WSAEventSelect() fails * below or in isc_socket_cancel(). */ addrlen = sizeof(from.type); fd = accept(sock->fd, &from.type.sa, &addrlen); if (fd != INVALID_SOCKET) { char addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_format(&from, addrbuf, sizeof(addrbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "sock->accept_list empty: " "dropping TCP request from %s", addrbuf); (void)closesocket(fd); } UNLOCK(&sock->lock); return; } /* * Try to accept the new connection. If the accept fails with * EAGAIN or EINTR, the event wait will be notified again since * the event will be reset on return to caller. */ 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); if (fd == INVALID_SOCKET) { accept_errno = WSAGetLastError(); if (SOFT_ERROR(accept_errno) || accept_errno == WSAECONNRESET) { goto soft_error; } else { isc__strerror(accept_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 = INVALID_SOCKET; result = ISC_R_UNEXPECTED; } } else { if (addrlen == 0) { UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_accept(): " "accept() failed to return " "remote address"); (void)closesocket(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)closesocket(fd); goto soft_error; } } if (fd != INVALID_SOCKET) { dev->newsocket->address.length = addrlen; dev->newsocket->pf = sock->pf; } /* * Pull off the done event. */ ISC_LIST_UNLINK(sock->accept_list, dev, ev_link); /* * Stop listing for connects. */ if (ISC_LIST_EMPTY(sock->accept_list) && WSAEventSelect(sock->fd, sock->hEvent, FD_CLOSE) != 0) { int stat; const char *msg; stat = WSAGetLastError(); isc__strerror(stat, strbuf, sizeof(strbuf)); msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"); UNEXPECTED_ERROR(__FILE__, __LINE__, "WSAEventSelect: %s: %s", msg, strbuf); } UNLOCK(&sock->lock); if (fd != INVALID_SOCKET) { isc_result_t tresult; tresult = make_nonblock(fd); if (tresult != ISC_R_SUCCESS) { closesocket(fd); fd = INVALID_SOCKET; result = tresult; } } /* * INVALID_SOCKET means the new socket didn't happen. */ if (fd != INVALID_SOCKET) { LOCK(&manager->lock); ISC_LIST_APPEND(manager->socklist, dev->newsocket, link); dev->newsocket->fd = fd; dev->newsocket->bound = 1; dev->newsocket->connected = 1; /* * The accept socket inherits the listen socket's * selected events. Remove this socket from all events * as it is handled by IOCP. (Joe Quanaim, lucent.com) */ if (WSAEventSelect(dev->newsocket->fd, 0, 0) != 0) { /* this is an unlikely but non-fatal error */ int stat; const char *msg; stat = WSAGetLastError(); isc__strerror(stat, strbuf, sizeof(strbuf)); msg = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed"); UNEXPECTED_ERROR(__FILE__, __LINE__, "WSAEventSelect: %s: %s", msg, strbuf); } /* * Save away the remote address */ dev->address = dev->newsocket->address; 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_t **)&dev); return; soft_error: UNLOCK(&sock->lock); return;}/* * Called when a socket with a pending connect() finishes. */static voidinternal_connect(isc_socket_t *sock, int connect_errno) { isc_socket_connev_t *dev; isc_task_t *task; char strbuf[ISC_STRERRORSIZE]; INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); /* * Has this event been canceled? */ dev = sock->connect_ev; if (dev == NULL) { INSIST(!sock->connecting); UNLOCK(&sock->lock); return; } INSIST(sock->connecting); sock->connecting = 0; /* * Check possible Windows network event error status here. */ if (connect_errno != 0) { /* * If the error is EAGAIN, just try again on this * fd and pretend nothing strange happened. */ if (SOFT_ERROR(connect_errno) || connect_errno == WSAEINPROGRESS) { sock->connecting = 1; UNLOCK(&sock->lock); return; } /* * Translate other errors into ISC_R_* flavors. */ switch (connect_errno) {#define ERROR_MATCH(a, b) case a: dev->result = b; break; ERROR_MATCH(WSAEACCES, ISC_R_NOPERM); ERROR_MATCH(WSAEADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); ERROR_MATCH(WSAEAFNOSUPPORT, ISC_R_ADDRNOTAVAIL); ERROR_MATCH(WSAECONNREFUSED, ISC_R_CONNREFUSED); ERROR_MATCH(WSAEHOSTUNREACH, ISC_R_HOSTUNREACH); ERROR_MATCH(WSAEHOSTDOWN, ISC_R_HOSTDOWN); ERROR_MATCH(WSAENETUNREACH, ISC_R_NETUNREACH); ERROR_MATCH(WSAENETDOWN, ISC_R_NETDOWN); ERROR_MATCH(WSAENOBUFS, ISC_R_NORESOURCES); ERROR_MATCH(WSAECONNRESET, ISC_R_CONNECTIONRESET); ERROR_MATCH(WSAECONNABORTED, ISC_R_CONNECTIONRESET); ERROR_MATCH(WSAETIMEDOUT, ISC_R_TIMEDOUT);#undef ERROR_MATCH default: dev->result = ISC_R_UNEXPECTED; isc__strerror(connect_errno, strbuf, sizeof(strbuf)); UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_connect: connect() %s", strbuf); } } else { dev->result = ISC_R_SUCCESS; sock->connected = 1; sock->bound = 1; } sock->connect_ev = NULL; UNLOCK(&sock->lock); task = dev->ev_sender; dev->ev_sender = sock; isc_task_sendanddetach(&task, (isc_event_t **)&dev);}static voidinternal_recv(isc_socket_t *sock, isc_socketevent_t *dev, struct msghdr *messagehdr, int nbytes, int recv_errno){ isc_socketevent_t *ldev; int io_state; int cc; INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); socket_log(sock, NULL, IOEVENT, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALRECV, "internal_recv: task got socket event %p", dev); INSIST(sock->pending_recv > 0); sock->pending_recv--; /* If the event is no longer in the list we can just return */ ldev = ISC_LIST_HEAD(sock->recv_list); while (ldev != NULL && ldev != dev) { ldev = ISC_LIST_NEXT(ldev, ev_link); } if (ldev == NULL) goto done; /* * Try to do as much I/O as possible on this socket. There are no * limits here, currently. */ switch (completeio_recv(sock, dev, messagehdr, nbytes, recv_errno)) { case DOIO_SOFT: cc = 0; recv_errno = 0; io_state = startio_recv(sock, dev, &cc, &recv_errno); goto done; 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. */ dev->result = ISC_R_EOF; send_recvdone_event(sock, &dev); goto done; case DOIO_SUCCESS: case DOIO_HARD: send_recvdone_event(sock, &dev); break; } done: UNLOCK(&sock->lock);}static voidinternal_send(isc_socket_t *sock, isc_socketevent_t *dev, struct msghdr *messagehdr, int nbytes, int send_errno){ isc_socketevent_t *ldev; int io_state; int cc; /* * Find out what socket this is and lock it. */ INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); socket_log(sock, NULL, IOEVENT, isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALSEND, "internal_send: task got socket event %p", dev); INSIST(sock->pending_send > 0); sock->pending_send--; /* If the event is no longer in the list we can just return */ ldev = ISC_LIST_HEAD(sock->send_list); while (ldev != NULL && ldev != dev) { ldev = ISC_LIST_NEXT(ldev, ev_link); } if (ldev == NULL) goto done; /* * Try to do as much I/O as possible on this socket. There are no * limits here, currently. */ switch (completeio_send(sock, dev, messagehdr, nbytes, send_errno)) { case DOIO_SOFT: cc = 0; send_errno = 0; io_state = startio_send(sock, dev, &cc, &send_errno); goto done; case DOIO_HARD: case DOIO_SUCCESS: send_senddone_event(sock, &dev); break; } done: UNLOCK(&sock->lock);}/* * This is the I/O Completion Port Worker Function. It loops forever * waiting for I/O to complete and then forwards them for further * processing. There are a number of these in separate threads. */static isc_threadresult_t WINAPISocketIoThread(LPVOID ThreadContext) { isc_socketmgr_t *manager = ThreadContext; BOOL bSuccess = FALSE; DWORD nbytes; IoCompletionInfo *lpo = NULL; isc_socket_t *sock = NULL; int request; isc_socketevent_t *dev = NULL; struct msghdr *messagehdr = NULL; int errval; char strbuf[ISC_STRERRORSIZE]; int errstatus; REQUIRE(VALID_MANAGER(manager)); /* Set the thread priority high enough so I/O will * preempt normal recv packet processing, but not * higher than the timer sync thread. */ if (!SetThreadPriority(GetCurrentThre
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -