vendredi 27 octobre 2017

`WSASend` never triggers completion routine if there was no preceding `WSARead`

I'm developing server-client application which manages req/res topics and does fan-out of some messages. Messages sends out with WSASend in overlapped mode with provided Completion routine. Noticed that very next send with WSASend after WSARead triggers sender's completion routine. While the sends originated in the server w/o preceding WSARead doesn't trigger completion routine. Why does that happen?

bool CCommunicationServer::ManageReadMessage(UCHAR index)
{
    OVERLAPPED_EX *over = m_readov[index];
    DWORD bytes = 0, flags = 0;

    if (WSARecv(over->socket, &(over->wsabuf), 1, &bytes, &flags, over, &CCommunicationServer::WorkerReadRoutine) == SOCKET_ERROR) {
        bytes = WSAGetLastError();

        if (bytes != WSA_IO_PENDING) {
            LOG(ERROR) << std::endl << "WSARecv() failed w/err " << bytes;
            shutdown(m_readov[index]->socket, SD_BOTH);
            return FALSE;
        }
    }

    return TRUE;
}

bool CCommunicationServer::ManageSendMessage(UCHAR index, char* msg, OVERLAPPED_EX *moreover)
{
    OVERLAPPED_EX *over = moreover;
    DWORD bytes = 0;
    SOCKET socket = m_readov[index] != NULL ? m_readov[index]->socket : NULL;

    if (!over) {
        ScopedLock lock(&m_lock);

        if (!msg) {
            LOG(WARNING) << "Empty send! Ignoring!";
            return FALSE;
        } else if (m_readov[index] != NULL) {
            LOG(DEBUG) << ++created << "\t:\t" << deleted << "\t\t\t\r";
            over = new OVERLAPPED_EX(this, m_readov[index]->socket, index, msg);
        } else {
            return FALSE;
        }
    }

    if (WSASend(socket, &(over->wsabuf), 1, &bytes, 0, (LPWSAOVERLAPPED)over, &CCommunicationServer::WorkerSendRoutine) == SOCKET_ERROR) {
        bytes = bytes = WSAGetLastError();

        if (bytes != WSA_IO_PENDING) {
            LOG(ERROR) << std::endl << "WSASend() failed w/err " << bytes;
            shutdown(socket, SD_BOTH);
            LOG(DEBUG) << created << "\t:\t" << ++deleted << "\t\t\t\r";
            delete over;
            return FALSE;
        }
    }

    return TRUE;
}

And here are Completion routines:

void CALLBACK CCommunicationServer::WorkerReadRoutine(DWORD Error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
    OVERLAPPED_EX *over = (OVERLAPPED_EX*)Overlapped;

    if (Error)
        return;

    if (BytesTransferred > 0) {
        over->server->GetRequest(over);
    }

    over->server->ManageReadMessage(over->index);
}

void CALLBACK CCommunicationServer::WorkerSendRoutine(DWORD Error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
    OVERLAPPED_EX *over = (OVERLAPPED_EX*)Overlapped;

    if (Error || over->wsabuf.len == BytesTransferred) {
        LOG(DEBUG) << over->server->created << "\t:\t" << ++over->server->deleted << "\t\t\t\r";
        delete over;
    } else {
        over->wsabuf.buf += BytesTransferred;
        over->wsabuf.len -= BytesTransferred;
        over->server->ManageSendMessage(over->index, NULL, over);
    }
}

The variables created and deleted shows how many OVERLAPPED_EX structures were created and then released to avoid memory leakage.

Aucun commentaire:

Enregistrer un commentaire