C++ for Simultaneous TLS or Plain Socket Read/Write from Different Threads (Windows)


#if defined(WIN32)

static int _numSockThreadsRunning = 0;
static bool _bTlsConnection = false;

static DWORD WINAPI SockThreadA(LPVOID lpvThreadParm)
    {
    CkSocket *pSock0 = (CkSocket *)lpvThreadParm;

    CkSocket *pSock = pSock0->CloneSocket();

    // Read the HTTP response header.
    while (true)
    {
    CkString strLine;
    bool success = pSock->ReceiveToCRLF(strLine);
    if (!success) 
        {
        printf("%s\n",pSock->lastErrorText());
        break;
        }
    printf("Received: %s",strLine.getString());
    if (strLine.equals("\r\n")) break;    // We have the full header..
    }
    delete pSock;
    _numSockThreadsRunning--;
    return 0;
    }

static DWORD WINAPI SockThreadB(LPVOID lpvThreadParm)
    {
    CkSocket *pSock0 = (CkSocket *)lpvThreadParm;
    CkSocket *pSock = pSock0->CloneSocket();

    while (_numSockThreadsRunning > 1)
    {
    DWORD startTick = GetTickCount();
    bool bConnected = pSock->get_IsConnected();
    DWORD endTick = GetTickCount();

    if ((endTick - startTick) > 500)
        {
        printf("ERROR -- IsConnected blocked.\n");
        break;
        }

    printf("bConnected: %d\n",bConnected);

    Sleep(200);
    }

    delete pSock;
    _numSockThreadsRunning--;
    return 0;
    }

static DWORD WINAPI SockThreadC(LPVOID lpvThreadParm)
    {
    CkSocket *pSock0 = (CkSocket *)lpvThreadParm;
    CkSocket *pSock = pSock0->CloneSocket();

    // Wait 3 seconds, and then send the GET request..
    // The socket read in thread A should not be blocking the IsConnected calls.
    Sleep(3000);

    CkString strGet;
    if (_bTlsConnection)
    {
    pSock->BuildHttpGetRequest("https://www.chilkatsoft.com/test.asp",strGet);
    }
    else
    {
    pSock->BuildHttpGetRequest("http://www.chilkatsoft.com/test.asp",strGet);
    }

    bool success = pSock->SendString(strGet.getString());
    if (!success)
    {
    printf("%s\n",pSock->lastErrorText());
    }

    delete pSock;
    _numSockThreadsRunning--;
    return 0;
    }

static bool startSockThread(char which, CkSocket *pSock)
    {
    bool success = false;
    DWORD threadID;
    HANDLE hThread = NULL;
    if (which == 'A') hThread = CreateThread(0,0,SockThreadA,(void *)pSock,0,&threadID);
    if (which == 'B') hThread = CreateThread(0,0,SockThreadB,(void *)pSock,0,&threadID);
    if (which == 'C') hThread = CreateThread(0,0,SockThreadC,(void *)pSock,0,&threadID);
    if (hThread != NULL)
    {
    CloseHandle(hThread);
    success = true;
    _numSockThreadsRunning++;
    }
    return success;
    }


bool SocketTesting::qa_simultaneousMtReadWrite(bool bTls)
    {
    CkSocket sock;

    _bTlsConnection = bTls;

    sock.put_VerboseLogging(true);
    //sock.put_DebugLogFilePath("c:/aaworkarea/debugLogPath.txt");

    bool success = false;
    if (bTls)
    {
    success = sock.Connect("www.chilkatsoft.com",443,true,5000);
    }
    else
    {
    success = sock.Connect("www.chilkatsoft.com",80,false,5000);
    }
    if (!success)
    {
    printf("%s\n",sock.lastErrorText());
    printf("qa_simultaneousMtReadWrite failed\n");
    return false;
    }

    success = startSockThread('A',&sock);
    if (success) success = startSockThread('B',&sock);
    if (success) success = startSockThread('C',&sock);
    if (!success)
    {
    printf("Failed to start threads.\n");
    printf("qa_simultaneousMtReadWrite failed\n");
    return false;
    }

    int loopCount = 0;
    while (_numSockThreadsRunning)
    {
    Sleep(100);
    loopCount++;
    if (loopCount > 200) 
        {
        // It shouldn't take this long...
        printf("qa_simultaneousMtReadWrite failed! TIMED OUT.\n");
        return false;
        }
    }

    printf("loopCount = %d\n",loopCount);

    printf("qa_simultaneousMtReadWrite success\n");
    return true;
    }

#endif