سؤال

I am currently trying some new libraries (IOCP) for socket programming. And I've stumbled upon the AcceptEx functionality to enable async connections.

As the documentation says:

The AcceptEx function uses overlapped I/O, unlike the accept function. If your application uses AcceptEx, it can service a large number of clients with a relatively small number of threads. As with all overlapped Windows functions, either Windows events or completion ports can be used as a completion notification mechanism.

But I am not receving any completion when a client connects. I do however get a completion when the client sends data..

This is my code:

DWORD dwBytes;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
int iResult = WSAIoctl(m_hSocket, SIO_GET_EXTENSION_FUNCTION_POINTER,
    &GuidAcceptEx, sizeof (GuidAcceptEx), 
    &m_lpfnAcceptEx, sizeof (m_lpfnAcceptEx), 
    &dwBytes, NULL, NULL);

if (iResult == SOCKET_ERROR)
{
    CloseSocket();
}

And then:

WSAOVERLAPPED olOverlap;
memset(&olOverlap, 0, sizeof (olOverlap));
char lpOutputBuf[1024];
int outBufLen = 1024;
DWORD dwBytes;

BOOL bRet = m_lpfnAcceptEx( m_hSocket, hSocket, lpOutputBuf,
             outBufLen - ((sizeof (sockaddr_in) + 16) * 2),
             sizeof (sockaddr_in) + 16, sizeof (sockaddr_in) + 16, 
             &dwBytes, &olOverlap);
if ( bRet == FALSE )
{
    DWORD dwRet = WSAGetLastError();
    if( dwRet != WSA_IO_PENDING )
    {
        return dwRet;
    }
}

Any suggestion of what to do to receive completions?

EDIT: I bind the hSocket to the completionport after m_lpfnAcceptEx()

هل كانت مفيدة؟

المحلول

Firstly the WSAOVERLAPPED and data buffer that you're declaring on the stack above your call to AcceptEx() will not be in existence when a completion occurs (unless you are calling GetQueuedCompletionStatus() in the same function, which would be a trifle odd). You need to dynamically allocate them or pool them.

Secondly you state that you associate the socket to the completion port after you call AcceptEx(). That's wrong. You need to do these things before you call AcceptEx().

  1. Create a socket with WSA_FLAG_OVERLAPPED set.
  2. Bind it to the address you want to listen on.
  3. Call listen on it with your desired backlog.
  4. Load AcceptEx() dynamically using the listening socket and a call to WSAIoctl (not strictly necessary and the code you show should work but this way you can be sure you get your listening socket from the same underlying winsock provider and that it supports AcceptEx().
  5. Load GetAcceptExSockaddrs() in the same way as you load AcceptEx() - you'll need it once the accept completes.
  6. Associate the listening socket to your IOCP.

Now you can post a number of AcceptEx() calls using the listening socket and new 'accept' socket which you create like this:

  1. Create a socket with WSA_FLAG_OVERLAPPED set.
  2. Associate the socket to your IOCP.

As stated above you need to ensure that the buffer and the OVERLAPPED are unique per call and last until the completion occurs.

When the completion occurs you have to do the following....

  1. Call setsockopt() with SO_UPDATE_ACCEPT_CONTEXT on the accepted socket using the listening socket as the data...
  2. Deblock your addresses using GetAcceptExSockaddrs().
  3. Process any data (if you allocated enough space in the buffer for data).

Note that by design AcceptEx() can be used to accept a new connection and return the initial data from that connection in one operation (this leads to slightly better performance in situations where you know you will always want some data before you can start doing things but is horribly complex to manage if you want to defend aginst the denial of service attack that can be launched simply by connecting and NOT sending data - I wrote about this here).

If you do not want AcceptEx() to wait for data to arrive then simply provide a data buffer that is ONLY big enough for the addresses to be returned and pass 0 as the 'buffer size'. This will cause the AcceptEx() to operate like an overlaped accept() and return as soon as the connection is established.

Note that Martin James' initial comment to your question is in fact the answer you're looking for. Don't pass outBufLen - ((sizeof (sockaddr_in) + 16) * 2), pass 0.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top