static void connectCallback(NetDescriptor *nd) { // Called by the NetManager when a connection has been established. ConnectState *connectState = (ConnectState *) NetDescriptor_getExtra(nd); int err; if (connectState->alarm != NULL) { Alarm_remove(connectState->alarm); connectState->alarm = NULL; } if (connectState->state == Connect_closed) { // The connection attempt has been aborted. #ifdef DEBUG log_add(log_Debug, "Connection attempt was aborted."); #endif ConnectState_decRef(connectState); return; } if (Socket_getError(NetDescriptor_getSocket(nd), &err) == -1) { log_add(log_Fatal, "Socket_getError() failed: %s.", strerror(errno)); explode(); } if (err != 0) { #ifdef DEBUG log_add(log_Debug, "connect() failed: %s.", strerror(err)); #endif NetDescriptor_close(nd); connectState->nd = NULL; connectState->infoPtr = connectState->infoPtr->ai_next; connectHostNext(connectState); return; } #ifdef DEBUG log_add(log_Debug, "Connection established."); #endif // Notify the higher layer. connectState->nd = NULL; // The callback function takes over ownership of the // NetDescriptor. NetDescriptor_setWriteCallback(nd, NULL); // Note that connectState->info and connectState->infoPtr are cleaned up // when ConnectState_close() is called by the callback function. ConnectState_incRef(connectState); doConnectCallback(connectState, nd, connectState->infoPtr->ai_addr, connectState->infoPtr->ai_addrlen); { // The callback called should release the last reference to // the connectState, by calling ConnectState_close(). bool released = ConnectState_decRef(connectState); assert(released); (void) released; // In case assert() evaluates to nothing. } }
static void acceptSingleConnection(ListenState *listenState, NetDescriptor *nd) { Socket *sock; Socket *acceptResult; struct sockaddr_storage addr; socklen_t addrLen; NetDescriptor *newNd; sock = NetDescriptor_getSocket(nd); addrLen = sizeof (addr); acceptResult = Socket_accept(sock, (struct sockaddr *) &addr, &addrLen); if (acceptResult == Socket_noSocket) { switch (errno) { case EWOULDBLOCK: case ECONNABORTED: // Nothing serious. Keep listening. return; case EMFILE: case ENFILE: case ENOBUFS: case ENOMEM: #ifdef ENOSR case ENOSR: #endif // Serious problems, but future connections may still // be possible. log_add(log_Warning, "accept() reported '%s'", strerror(errno)); return; default: // Should not happen. log_add(log_Fatal, "Internal error: accept() reported " "'%s'", strerror(errno)); explode(); } } (void) Socket_setReuseAddr(acceptResult); // Ignore errors; it's not a big deal. if (Socket_setNonBlocking(acceptResult) == -1) { int savedErrno = errno; log_add(log_Error, "Could not make socket non-blocking: %s.", strerror(errno)); Socket_close(acceptResult); errno = savedErrno; return; } (void) Socket_setInlineOOB(acceptResult); // Ignore errors; it's not a big deal as the other // party is not not supposed to send any OOB data. (void) Socket_setKeepAlive(sock); // Ignore errors; it's not a big deal. #ifdef DEBUG { char hostname[NI_MAXHOST]; int gniRes; gniRes = getnameinfo((struct sockaddr *) &addr, addrLen, hostname, sizeof hostname, NULL, 0, 0); if (gniRes != 0) { log_add(log_Error, "Error while performing hostname " "lookup for incoming connection: %s", (gniRes == EAI_SYSTEM) ? strerror(errno) : gai_strerror(gniRes)); } else { log_add(log_Debug, "Accepted incoming connection from '%s'.", hostname); } } #endif newNd = NetDescriptor_new(acceptResult, NULL); if (newNd == NULL) { int savedErrno = errno; log_add(log_Error, "NetDescriptor_new() failed: %s.", strerror(errno)); Socket_close(acceptResult); errno = savedErrno; return; } doListenConnectCallback(listenState, nd, newNd, (struct sockaddr *) &addr, addrLen); // NB: newNd is now handed over to the callback function, and should // no longer be referenced from here. }
void dataReadyCallback(NetDescriptor *nd) { NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd); Socket *socket = NetDescriptor_getSocket(nd); for (;;) { ssize_t numRead; ssize_t numProcessed; numRead = Socket_recv(socket, conn->readEnd, NETPLAY_READBUFSIZE - (conn->readEnd - conn->readBuf), 0); if (numRead == 0) { // Other side closed the connection. NetDescriptor_close(nd); return; } if (numRead == -1) { switch (errno) { case EAGAIN: // No more data for now. return; case EINTR: // System call was interrupted. Retry. continue; default: { int savedErrno = errno; log_add(log_Error, "recv() failed: %s.\n", strerror(errno)); NetConnection_doErrorCallback(conn, savedErrno); NetDescriptor_close(nd); return; } } } conn->readEnd += numRead; numProcessed = dataReceivedMulti(conn, conn->readBuf, conn->readEnd - conn->readBuf); if (numProcessed == -1) { // An error occured during processing. // errno is set. NetConnection_doErrorCallback(conn, errno); NetDescriptor_close(nd); return; } if (numProcessed == 0) { // No packets could be processed. This means we need to receive // more data first. return; } // Some packets have been processed. // We more any rest to the front of the buffer, to make room // for more data. // A cyclic buffer would obviate the need for this move, // but it would complicate things a lot. memmove(conn->readBuf, conn->readBuf + numProcessed, (conn->readEnd - conn->readBuf) - numProcessed); conn->readEnd -= numProcessed; } }