static void test1() { RTTestSub(g_hTest, "Simple server-client setup"); /* * Set up server address (port) for UDP. */ RTNETADDR ServerAddress; RTTESTI_CHECK_RC_RETV(RTSocketParseInetAddress(RT_TEST_UDP_LOCAL_HOST, RT_TEST_UDP_SERVER_PORT, &ServerAddress), VINF_SUCCESS); PRTUDPSERVER pServer; RTTESTI_CHECK_RC_RETV(RTUdpServerCreate(RT_TEST_UDP_LOCAL_HOST, RT_TEST_UDP_SERVER_PORT, RTTHREADTYPE_DEFAULT, "server-1", test1Server, NULL, &pServer), VINF_SUCCESS); int rc; RTSOCKET hSocket; RTTESTI_CHECK_RC(rc = RTUdpCreateClientSocket(RT_TEST_UDP_LOCAL_HOST, RT_TEST_UDP_SERVER_PORT, NULL, &hSocket), VINF_SUCCESS); if (RT_SUCCESS(rc)) { do /* break non-loop */ { char szBuf[512]; RT_ZERO(szBuf); RTTESTI_CHECK_RC_BREAK(RTSocketWrite(hSocket, "dude!\n", sizeof("dude!\n") - 1), VINF_SUCCESS); RTTESTI_CHECK_RC_BREAK(RTSocketRead(hSocket, szBuf, sizeof("hello\n") - 1, NULL), VINF_SUCCESS); szBuf[sizeof("hello!\n") - 1] = '\0'; RTTESTI_CHECK_BREAK(strcmp(szBuf, "hello\n") == 0); RTTESTI_CHECK_RC_BREAK(RTSocketWrite(hSocket, "byebye\n", sizeof("byebye\n") - 1), VINF_SUCCESS); RT_ZERO(szBuf); RTTESTI_CHECK_RC_BREAK(RTSocketRead(hSocket, szBuf, sizeof("buh bye\n") - 1, NULL), VINF_SUCCESS); RTTESTI_CHECK_BREAK(strcmp(szBuf, "buh bye\n") == 0); } while (0); RTTESTI_CHECK_RC(RTSocketClose(hSocket), VINF_SUCCESS); } RTTESTI_CHECK_RC(RTUdpServerDestroy(pServer), VINF_SUCCESS); }
/** @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; }
/** * Destruct a TCP socket stream driver instance. * * Most VM resources are freed by the VM. This callback is provided so that * any non-VM resources can be freed correctly. * * @param pDrvIns The driver instance data. */ static DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns) { PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP); LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation)); PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); drvTCPShutdownListener(pThis); /* * While the thread exits, clean up as much as we can. */ if (pThis->hTcpSock != NIL_RTSOCKET) { int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); AssertRC(rc); rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */); AssertRC(rc); rc = RTSocketClose(pThis->hTcpSock); AssertRC(rc); RT_NOREF(rc); pThis->hTcpSock = NIL_RTSOCKET; } if (pThis->hPipeWakeR != NIL_RTPIPE) { int rc = RTPipeClose(pThis->hPipeWakeR); AssertRC(rc); pThis->hPipeWakeR = NIL_RTPIPE; } if (pThis->hPipeWakeW != NIL_RTPIPE) { int rc = RTPipeClose(pThis->hPipeWakeW); AssertRC(rc); pThis->hPipeWakeW = NIL_RTPIPE; } if (pThis->hPollSet != NIL_RTPOLLSET) { int rc = RTPollSetDestroy(pThis->hPollSet); AssertRC(rc); pThis->hPollSet = NIL_RTPOLLSET; } MMR3HeapFree(pThis->pszLocation); pThis->pszLocation = NULL; /* * Wait for the thread. */ if (pThis->ListenThread != NIL_RTTHREAD) { int rc = RTThreadWait(pThis->ListenThread, 30000, NULL); if (RT_SUCCESS(rc)) pThis->ListenThread = NIL_RTTHREAD; else LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, 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; }