[ create a new paste ] login | about

Link: http://codepad.org/pAxiBD7K    [ raw code | fork ]

greg - C++, pasted on Feb 4:
/*
  This example demonstrates two problems with WSARecv and IOCP on Vista and up.
  I'm using Windows 7 Ultimate, and it still occurs here. One problem occurs
  when using FILE_SKIP_COMPLETION_PORT_ON_SUCCESS and one is when it is not in
  use. This example runs fine on XP, obviously with USE_SKIP_FEATURE = 0.

  The example simply accepts a socket and reads from it as fast as possible.

  With USE_SKIP_FEATURE = 1, it eventually (5-30 seconds) gets a timeout from
  GetQueuedCompletionStatus, which should not occur. You should see this example
  die with:
    queueing new event
    event completion pending
    GQCS wait timeout?

  With USE_SKIP_FEATURE = 0, it quickly (1-5 seconds) gets a return value of 0
  from WSARecv with lpNumberOfBytesRecvd = 0, indicating socket closure, which
  should not be true. You should see this example will die with:
    queueing new event
    event completed right away: 0 bytes
    eof

  MSDN says:

    http://msdn.microsoft.com/en-us/library/ms741688(VS.85).aspx

    "For byte streams, zero bytes having been read (as indicated by a zero
     return value to indicate success, and lpNumberOfBytesRecvd value of zero)
     indicates graceful closure and that no more bytes will ever be read."

    ...

    "Overlapped Socket I/O

     If an overlapped operation completes immediately, WSARecv returns a value
     of zero and the lpNumberOfBytesRecvd parameter is updated with the number
     of bytes received and the flag bits indicated by the lpFlags parameter are
     also updated."

  I recommend directing the output of this example to a file, to prevent the
  console from slowing down the the application and making the bug difficult to
  encounter.

  For the connecting and sending side, anything which connects to localhost port
  27015 and sends as fast as it can should do. Here is a very simple python
  script:

    import socket
    s = socket.socket()
    s.connect(("localhost", 27015))
    while 1:
      s.send(" " * 65536)

  In both cases the sender dies with:
    socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host

  If there are any questions or obvious bugs, please contact me at: ghazel at gmail dot com
*/

#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include "winsock2.h"
#include "mswsock.h"
#include "malloc.h"
#include <windows.h>

#define BUFSIZE 16384
#define MAX_PENDING 5

#define USE_SKIP_FEATURE 0

void main()
{
  //----------------------------------------
  // Declare and initialize variables
  WSADATA wsaData;
  HANDLE hCompPort;

  SOCKET ListenSocket, AcceptSocket;
  sockaddr_in service;

  sockaddr a_addr;
  int a_addrlen = sizeof(a_addr);

  printf("use skip feature: %d\n", USE_SKIP_FEATURE);

  //----------------------------------------
  // Initialize Winsock
  int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
  if (iResult != NO_ERROR) {
    printf("Error at WSAStartup\n");
    return;
  }

  //----------------------------------------
  // Create a handle for the completion port
  hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long)0, 0);

  //----------------------------------------
  // Create a listening socket
  ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (ListenSocket == INVALID_SOCKET) {
    printf("Error at socket(): ListenSocket\n");
    WSACleanup();
    return;
  }

  //----------------------------------------
  // Associate the listening socket with the completion port
  CreateIoCompletionPort((HANDLE)ListenSocket, hCompPort, (u_long)0, 0);

  //----------------------------------------
  // Bind the listening socket to the local IP address
  // and port 27015
  hostent* thisHost;
  char* ip;
  u_short port;
  port = 27015;
  thisHost = gethostbyname("localhost");
  ip = inet_ntoa (*(struct in_addr *)*thisHost->h_addr_list);

  service.sin_family = AF_INET;
  service.sin_addr.s_addr = inet_addr(ip);
  service.sin_port = htons(port);

  if (bind(ListenSocket,(SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) {
    printf("bind failed\n");
    closesocket(ListenSocket);
    return;
  }

  //----------------------------------------
  // Start listening on the listening socket
  if (listen( ListenSocket, 100 ) == SOCKET_ERROR) {
    printf("error listening\n");
  }
  printf("Listening on address: %s:%d\n", ip, port);

  AcceptSocket = accept(ListenSocket, &a_addr, &a_addrlen);
  if (AcceptSocket == INVALID_SOCKET) {
    printf("accept failed\n");
    return;
  }

  CreateIoCompletionPort((HANDLE)AcceptSocket, hCompPort, (u_long)0, 0);

#if USE_SKIP_FEATURE
  if (!SetFileCompletionNotificationModes((HANDLE)AcceptSocket, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
    printf("SetFileCompletionNotificationModes failed! %d\n", GetLastError());
    return;
  }
#endif

  int pending_completions = 0;
  while (1) {
    DWORD bytes;
    DWORD key;
    DWORD rc;
    DWORD flags = 0;
    char buf[BUFSIZE];
    WSABUF wsabuf;
    WSAOVERLAPPED *ov = (WSAOVERLAPPED *)malloc(sizeof(WSAOVERLAPPED));

    memset(ov, 0, sizeof(WSAOVERLAPPED));
    wsabuf.len = 16384;
    wsabuf.buf = buf;

    printf("queueing new event\n");
    rc = WSARecv(AcceptSocket, &wsabuf, 1, &bytes, &flags, ov, 0);
    if (rc == SOCKET_ERROR) {
      rc = WSAGetLastError();
      if (rc != WSA_IO_PENDING) {
        printf("WSARecv error (a): %d\n", rc);
        return;
      }
      pending_completions += 1;
      printf("event completion pending\n");
    } else if (rc != 0) {
      printf("WSARecv error (b): %d\n", rc);
      return;
    }

    if (rc == 0) {
#if USE_SKIP_FEATURE
      // no completion pending notice, because of FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
      free(ov);
#else
      // we still need to receive the completion notice, even though the event is complete
      pending_completions += 1;
#endif
      printf("event completed right away: %u bytes\n", bytes);
      if (bytes == 0) {
        printf("eof\n");
        return;
      }
      if (pending_completions < MAX_PENDING) {
        printf("currently pending: %u of %u\n", pending_completions, MAX_PENDING);
        continue;
      }
    }

    while (pending_completions) {
      WSAOVERLAPPED *ovp;
      rc = GetQueuedCompletionStatus(hCompPort, &bytes, &key, ((OVERLAPPED **)(&ovp)), 2000);
      if (!rc) {
        rc = GetLastError();
        if (rc == WAIT_TIMEOUT) {
          printf("GQCS wait timeout?\n");
          // Two seconds should be plenty, when the sender is local and just in a tight-loop.
          //continue;
          return;
        }
        printf("GQCS error: %d\n", rc);
        if (rc == ERROR_NETNAME_DELETED) {
          printf("eof\n");
        }
        return;
      }
      pending_completions -= 1;
      printf("event complete: (%u bytes) pending: %u of %u\n", bytes, pending_completions, MAX_PENDING);
      free(ovp);
    }
  }
}


Create a new paste based on this one


Comments: