示例#1
0
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;
}