📄 socket.c
字号:
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; INSIST(sock->references > 0); sock->references--; /* the internal event is done with this socket */ if (sock->references == 0) { UNLOCK(&sock->lock); destroy_socket(&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, 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); UNLOCK(&sock->lock); if (fd != INVALID_SOCKET && (make_nonblock(fd) != ISC_R_SUCCESS)) { closesocket(fd); fd = INVALID_SOCKET; result = ISC_R_UNEXPECTED; } /* * 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; /* * 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; int cc; ISC_SOCKADDR_LEN_T optlen; char strbuf[ISC_STRERRORSIZE]; INSIST(VALID_SOCKET(sock)); LOCK(&sock->lock); WSAResetEvent(sock->hEvent); /* * When the internal event was sent the reference count was bumped * to keep the socket around for us. Decrement the count here. */ INSIST(sock->references > 0); sock->references--; if (sock->references == 0) { UNLOCK(&sock->lock); destroy_socket(&sock); return; } /* * 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; /* * Get any possible error status here. */ optlen = sizeof(cc); if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void *)&cc, (void *)&optlen) < 0) connect_errno = WSAGetLastError(); else connect_errno = 0; 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_HOSTUNREACH); ERROR_MATCH(WSAENETUNREACH, ISC_R_NETUNREACH); ERROR_MATCH(WSAENOBUFS, ISC_R_NORESOURCES); ERROR_MATCH(EPERM, ISC_R_HOSTUNREACH); ERROR_MATCH(EPIPE, ISC_R_NOTCONNECTED); 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->references > 0); sock->references--; /* the internal event is done with this socket */ if (sock->references == 0) { UNLOCK(&sock->lock); destroy_socket(&sock); return; } /* 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, FALSE, &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->references > 0); sock->references--; /* the internal event is done with this socket */ if (sock->references == 0) { UNLOCK(&sock->lock); destroy_socket(&sock); return; } /* 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, FALSE, &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; DWORD tbytes; DWORD tflags; 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(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)) { errval = GetLastError(); isc__strerror(errval, strbuf, sizeof(strbuf)); FATAL_ERROR(__FILE__, __LINE__, isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_FAILED, "Can't set thread priority: %s"), strbuf); } /* * Loop forever waiting on I/O Completions and then processing them */ while(TRUE) { bSuccess = GetQueuedCompletionStatus ( manager->hIoCompletionPort, &nbytes, (LPDWORD) &sock, (LPOVERLAPPED *)&lpo, INFINITE ); if(lpo == NULL ) { /* * Received request to exit */ break; } errstatus = 0; if(!bSuccess) { /* * I/O Failure * Find out why */ WSAGetOverlappedResult(sock->fd, (LPWSAOVERLAPPED) &lpo, &tbytes, FALSE, &tflags); dev = lpo->dev; } request = lpo->request_type; dev = lpo->dev; messagehdr = &lpo->messagehdr; switch (request) { case SOCKET_CANCEL: break; case SOCKET_RECV: internal_recv(sock, dev, messagehdr, nbytes, errstatus); break; case SOCKET_SEND: internal_send(sock, dev, messagehdr, nbytes, errstatus); break; default: break; /* Unknown: Just ignore it */ } if (lpo != NULL) HeapFree(hHeapHandle, 0, lpo); } /* * Exit Completion Port Thread */ manager_log(manager, TRACE, isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_EXITING, "SocketIoThread exiting")); return ((isc_threadresult_t)0);}/* * This is the thread that will loop forever, waiting for an event to * happen. * * When the wait returns something to do, find the signaled event * and issue the request for the given socket */static isc_threadresult_t WINAPIevent_wait(void *uap) { events_thread_t *evthread = uap; isc_socketmgr_t *manager = evthread->manager; int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -