/** * @interface_method_impl{TXSTRANSPORT,pfnTerm} */ static DECLCALLBACK(void) txsTcpTerm(void) { /* Signal thread */ if (RTCritSectIsInitialized(&g_TcpCritSect)) { RTCritSectEnter(&g_TcpCritSect); g_fTcpStopConnecting = true; RTCritSectLeave(&g_TcpCritSect); } if (g_hThreadTcpConnect != NIL_RTTHREAD) { RTThreadUserSignal(g_hThreadTcpConnect); RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie); } /* Shut down the server (will wake up thread). */ if (g_pTcpServer) { Log(("txsTcpTerm: Destroying server...\n")); int rc = RTTcpServerDestroy(g_pTcpServer); if (RT_FAILURE(rc)) RTMsgInfo("RTTcpServerDestroy failed in txsTcpTerm: %Rrc", rc); g_pTcpServer = NULL; } /* Shut down client */ if (g_hTcpClient != NIL_RTSOCKET) { if (g_fTcpClientFromServer) { Log(("txsTcpTerm: Disconnecting client...\n")); int rc = RTTcpServerDisconnectClient2(g_hTcpClient); if (RT_FAILURE(rc)) RTMsgInfo("RTTcpServerDisconnectClient2(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc); } else { int rc = RTTcpClientClose(g_hTcpClient); if (RT_FAILURE(rc)) RTMsgInfo("RTTcpClientClose(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc); } g_hTcpClient = NIL_RTSOCKET; } /* Clean up stashing. */ RTMemFree(g_pbTcpStashed); g_pbTcpStashed = NULL; g_cbTcpStashed = 0; g_cbTcpStashedAlloced = 0; /* Wait for the thread (they should've had some time to quit by now). */ txsTcpConnectWaitOnThreads(15000); /* Finally, clean up the critical section. */ if (RTCritSectIsInitialized(&g_TcpCritSect)) RTCritSectDelete(&g_TcpCritSect); Log(("txsTcpTerm: done\n")); }
/** * Receive thread loop. * * @returns VINF_SUCCESS * @param hThreadSelf Thread handle to this thread. * @param pvUser User argument. */ static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD hThreadSelf, void *pvUser) { RT_NOREF(hThreadSelf); PDRVTCP pThis = (PDRVTCP)pvUser; while (RT_LIKELY(!pThis->fShutdown)) { RTSOCKET hTcpSockNew = NIL_RTSOCKET; int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew); if (RT_SUCCESS(rc)) { if (pThis->hTcpSock != NIL_RTSOCKET) { LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance)); RTTcpServerDisconnectClient2(hTcpSockNew); } else { pThis->hTcpSock = hTcpSockNew; /* Inform the poller about the new socket. */ drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION); } } } return VINF_SUCCESS; }
/** * Disconnects the current client. */ static void txsTcpDisconnectClient(void) { int rc; if (g_fTcpClientFromServer) rc = RTTcpServerDisconnectClient2(g_hTcpClient); else rc = RTTcpClientClose(g_hTcpClient); AssertRCSuccess(rc); g_hTcpClient = NIL_RTSOCKET; }
/** * Server mode connection thread. * * @returns iprt status code. * @param hSelf Thread handle. Ignored. * @param pvUser Ignored. */ static DECLCALLBACK(int) txsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser) { RTSOCKET hTcpClient; int rc = RTTcpServerListen2(g_pTcpServer, &hTcpClient); Log(("txsTcpConnectServerThread: RTTcpServerListen2 -> %Rrc\n", rc)); if (RT_SUCCESS(rc)) { hTcpClient = txsTcpSetClient(hTcpClient, true /*fFromServer*/); RTTcpServerDisconnectClient2(hTcpClient); } return rc; }
/** @interface_method_impl{PDMISTREAM,pfnRead} */ static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead) { int rc = VINF_SUCCESS; PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation)); Assert(pvBuf); if (pThis->hTcpSock != NIL_RTSOCKET) { size_t cbRead; size_t cbBuf = *pcbRead; rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead); if (RT_SUCCESS(rc)) { if (!cbRead && rc != VINF_TRY_AGAIN) { rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); AssertRC(rc); if (pThis->fIsServer) RTTcpServerDisconnectClient2(pThis->hTcpSock); else RTSocketClose(pThis->hTcpSock); pThis->hTcpSock = NIL_RTSOCKET; pThis->fTcpSockInPollSet = false; rc = VINF_SUCCESS; } *pcbRead = cbRead; } } else { RTThreadSleep(100); *pcbRead = 0; } LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc)); return rc; }
/** @interface_method_impl{PDMISTREAM,pfnPoll} */ static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies) { int rc = VINF_SUCCESS; PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); if (pThis->hTcpSock != NIL_RTSOCKET) { if (!pThis->fTcpSockInPollSet) { rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, fEvts, DRVTCP_POLLSET_ID_SOCKET); if (RT_SUCCESS(rc)) { pThis->fTcpSockInPollSet = true; pThis->fXmitBufFull = false; } } else { /* Always include error event. */ fEvts |= RTPOLL_EVT_ERROR; rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts); AssertRC(rc); } } if (RT_SUCCESS(rc)) { while (RT_SUCCESS(rc)) { uint32_t fEvtsRecv = 0; uint32_t idHnd = 0; /* * Just check for data available to be read if the send buffer wasn't full till now and * the caller wants to check whether writing is possible with the event set. * * On Windows the write event is only posted after a send operation returned * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting * for an event which would never happen if the buffer has space left. */ if ( (fEvts & RTPOLL_EVT_WRITE) && !pThis->fXmitBufFull && pThis->fTcpSockInPollSet) cMillies = 0; rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd); if (RT_SUCCESS(rc)) { if (idHnd == DRVTCP_POLLSET_ID_WAKEUP) { /* We got woken up, drain the pipe and return. */ uint8_t bReason; size_t cbRead = 0; rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead); AssertRC(rc); if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL) rc = VERR_INTERRUPTED; else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION) { Assert(!pThis->fTcpSockInPollSet); rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, fEvts, DRVTCP_POLLSET_ID_SOCKET); if (RT_SUCCESS(rc)) pThis->fTcpSockInPollSet = true; } else AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason)); } else { Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET); /* On error we close the socket here. */ if (fEvtsRecv & RTPOLL_EVT_ERROR) { rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); AssertRC(rc); if (pThis->fIsServer) RTTcpServerDisconnectClient2(pThis->hTcpSock); else RTSocketClose(pThis->hTcpSock); pThis->hTcpSock = NIL_RTSOCKET; pThis->fTcpSockInPollSet = false; /* Continue with polling. */ } else { if (fEvtsRecv & RTPOLL_EVT_WRITE) pThis->fXmitBufFull = false; else if (!pThis->fXmitBufFull) fEvtsRecv |= RTPOLL_EVT_WRITE; *pfEvts = fEvtsRecv; break; } } } else if ( rc == VERR_TIMEOUT && !pThis->fXmitBufFull) { *pfEvts = RTPOLL_EVT_WRITE; rc = VINF_SUCCESS; break; } } } return rc; }