tags:

views:

119

answers:

2

I am writing a multithreaded client that uses an IO Completion Port.

I create and connect the socket that has the WSA_FLAG_OVERLAPPED attribute set.

if ((m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
    throw std::exception("Failed to create socket.");
}

if (WSAConnectByName(m_socket, L"server.com", L"80", &localAddressLength, reinterpret_cast<sockaddr*>(&localAddress), &remoteAddressLength, &remoteAddress, NULL, NULL) == FALSE)
{
    throw std::exception("Failed to connect.");
}

I associate the IO Completion Port with the socket.

if ((m_hIOCP = CreateIoCompletionPort(reinterpret_cast<HANDLE>(m_socket), m_hIOCP, NULL, 8)) == NULL)
{
    throw std::exception("Failed to create IOCP object.");
}

All appears to go well until I try to send some data over the socket.

SocketData* socketData = new SocketData;
socketData->hEvent = 0;

DWORD bytesSent = 0;
if (WSASend(m_socket, socketData->SetBuffer(socketData->GenerateLoginRequestHeader()), 1, &bytesSent, NULL, reinterpret_cast<OVERLAPPED*>(socketData), NULL) == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING)
{
    throw std::exception("Failed to send data.");
}

Instead of returning SOCKET_ERROR with the last error set to WSA_IO_PENDING, WSASend returns immediately.

I need the IO to pend and for it's completion to be handled in my thread function which is also my worker thread.

unsigned int __stdcall MyClass::WorkerThread(void* lpThis)
{

}

I've done this before but I don't know what is going wrong in this case, I'd greatly appreciate any efforts in helping me fix this problem.

A: 

It's not a problem unless you make it so.

As long as you're not calling SetFileCompletionNotificationModes() and setting the flag to skip completion port processing on success then even if WSARecv (or whatever) returns SUCCESS an IO Completion Packet is queued to the IOCP the same as if ERROR_IO_PENDING was returned. Thus you need no special handling for the non error return case.

See http://support.microsoft.com/default.aspx?scid=kb;en-us;Q192800 for details.

Len Holgate
Well the issue was that WSARecv was returning immediately instead of posting to the IOCP queue. The last error returned was 0, as it should be ERROR_IO_PENDING. I've solved the problem by using ConnectEx instead of WSAConnect. Thank you for your reply :)
Carl
My point is that WSARecv CAN LEGALLY return 0 and IT STILL posts an IO completion in the same way that it does when it returns ERROR_IO_PENDING, unless you have adjusted how it works by calling SetFleCompletionNotificationModes() which allows you to tell it NOT to post an IO completion when it returns success... You ONLY have to have special case code for the success return IF you have called SetFileCompletionNotificationModes() with the appropriate flags.
Len Holgate
Ah, I didn't know that. Alright thank you! =]
Carl
I just read over your post again. I believe that it was returning 0 and still not posting to the IOCP queue.
Carl
A: 

First of all break the call into more clear logic:

int nRet = WSASend(m_socket, socketData->SetBuffer(socketData->GenerateLoginRequestHeader()), 1, NULL, NULL, reinterpret_cast<OVERLAPPED*>(socketData), NULL);
if (nRet == SOCKET_ERROR)
{
    if ((WSAGetLastError()) == WSA_IO_PENDING)
        nRet = 0; // ok
    else
        throw std::exception("Failed to send data."); // failed
}

Also, as you can see in my code, you should NOT pass the "&bytesSent" parameter according to WSASend:

Use NULL for this parameter if the lpOverlapped parameter is not NULL to avoid potentially erroneous results.

Besides that your call to WSASend() looks fine.

Poni
Alright, I'll change it. Thank you!
Carl