WinSockでpollをエミュレート

こんな感じ。

#include <stdio.h>
#include <winsock2.h>
#include "wspoll.h"

#include <memory.h>

#define ECHO_PORT 7
#define MAX_BACKLOG 5
#define RCVBUFSIZE 256
#define MAX_NFDS WSA_MAXIMUM_WAIT_EVENTS
#define POLL_TIMEOUT 1000

#define die(...) do { fprintf(stderr, __VA_ARGS__); WSACleanup(); exit(1); } while(0)
#define error(...) do { fprintf(stderr, __VA_ARGS__); } while(0)

static void wsa_startup(WORD version) {
  int n;
  WSADATA wsaData;

  if ((n = WSAStartup(version, &wsaData)) != 0) {
    die("WASStartup(): %d\n", n);
  }

  if (version != wsaData.wVersion) {
    die("WASStartup(): WinSock version %d.%d not supported\n", LOWORD(version), HIWORD(version));
  }
}

int echo(SOCKET sock) {
  char buf[RCVBUFSIZE + 1];
  size_t len;

  if ((len = recv(sock, buf, RCVBUFSIZE, 0)) < 0) {
    error("recv(): %d\n", WSAGetLastError());
    return -1;
  }

  if (len == 0) { return 0; }

  buf[len] = '\0';
  error("recv: %d '%s'\n", sock, buf);

  if (send(sock, buf, len, 0) < 0) {
    error("send(): %d\n", WSAGetLastError());
    return -1;
  }

  return len;
}


int main() {
  int i;
  SOCKET sock;
  SOCKET sockets[MAX_NFDS];
  struct sockaddr_in addr;

  wsa_startup(MAKEWORD(2, 0));

  if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
    die("socket(): %d\n", WSAGetLastError());
  }

  addr.sin_family = AF_INET;
  addr.sin_port = htons(ECHO_PORT);
  addr.sin_addr.S_un.S_addr = INADDR_ANY;

  if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
    die("bind(): %d\n", WSAGetLastError());
  }

  if (listen(sock, MAX_BACKLOG) != 0) {
    die("listen(): %d\n", WSAGetLastError());
  }

  memset(sockets, 0, sizeof(sockets));
  for (i = 0; i < MAX_NFDS; i++) { sockets[i] = INVALID_SOCKET; }
  sockets[0] = sock;

  while(1) {
    int i, j, nfds = 0;
    struct sockaddr_in sa;
    size_t len = sizeof(sa);
    struct pollfd pollfds[MAX_NFDS];

    for (i = 0; i < MAX_NFDS; i++) {
      if (sockets[i] != INVALID_SOCKET) {
        pollfds[nfds].fd = sockets[i];
        pollfds[nfds].events = POLLIN;
        nfds++;
      }
    }

    switch (poll(pollfds, nfds, POLL_TIMEOUT)) {
      case 0:
        continue;
      case -1:
        die("poll(2)\n");
    }

    for (i = 0; i < nfds; i++) {
      int fd = pollfds[i].fd;

      if (!(pollfds[i].revents & POLLIN)) { continue; }

      if (sock == fd) {
        int s;

        if ((s = accept(sock, (struct sockaddr*) &sa, &len)) < 0) {
          die("accept(2)\n");
        }

        error("accepted.\n");

        for (j = 0; j < MAX_NFDS; j++) {
          if (sockets[j] == INVALID_SOCKET) {
            sockets[j] = s;
            break;
          }
        }

        if (j >= MAX_NFDS) {
          closesocket(s);
        }
      } else {
        if (echo(fd) < 1) {
          closesocket(fd);
          sockets[i] = INVALID_SOCKET;
        }
      }
    }
  }

  closesocket(sock);
  WSACleanup();
  return 0;
}

wspoll.h

#include <winsock2.h>

#ifndef _WSPOLL_H_
#define _WSPOLL_H_

#ifdef POLLRDNORM
  #undef POLLRDNORM
  #undef POLLRDBAND
  #undef POLLIN
  #undef POLLPRI
  #undef POLLWRNORM
  #undef POLLOUT
  #undef POLLWRBAND
  #undef POLLERR
  #undef POLLHUP
  #undef POLLNVAL
  struct _pollfd {
    SOCKET fd;
    short  events;
    short  revents;
  };
  #define pollfd _pollfd
#else
  struct pollfd {
    SOCKET fd;
    short  events;
    short  revents;
  };
#endif

typedef unsigned int nfds_t;

#define POLLIN     (FD_READ | FD_ACCEPT | FD_CLOSE)
#define POLLPRI    (FD_OOB)
#define POLLOUT    (FD_WRITE | FD_CONNECT | FD_CLOSE)
#define POLLRDHUP  (FD_CLOSE)
#define POLLHUP    (FD_CLOSE)
#define POLLRDNORM (POLLIN)
#define POLLRDBAND (POLLIN | POLLPRI)
#define POLLWRNORM (POLLOUT)
#define POLLWRBAND (POLLOUT | POLLPRI)
// POLLERR, POLLNVAL not defined

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

#endif

wspoll.c

#include <malloc.h>
#include <errno.h>
#include "wspoll.h"

static int init(struct pollfd *pollfds, nfds_t nfds, SOCKET *fds, HANDLE *hEvents) {
  nfds_t i;

  for (i = 0; i < nfds; i++) {
    fds[i] = INVALID_SOCKET;
    hEvents[i] = NULL;
  }

  for (i = 0; i < nfds; i++) {
    fds[i] = pollfds[i].fd;
    hEvents[i] = WSACreateEvent();
    pollfds[i].revents = 0;

    if (WSAEventSelect(fds[i], hEvents[i], pollfds[i].events) < 0) {
      errno = WSAGetLastError();
      return -1;
    }
  }

  return 0;
}

static void clean(nfds_t nfds, SOCKET *fds, HANDLE *hEvents) {
  nfds_t i;

  for (i = 0; i < nfds; i++) {
    if (fds[i] != INVALID_SOCKET) {
      WSAEventSelect(fds[i], NULL, 0);
    }

    if (hEvents[i] != NULL) {
      WSACloseEvent(hEvents[i]);
    }
  }
}

int poll(struct pollfd *pollfds, nfds_t nfds, int timeout) {
  SOCKET *fds;
  HANDLE *hEvents;
  DWORD n;

  fds = alloca(sizeof(SOCKET) * nfds);
  hEvents = alloca(sizeof(HANDLE) * nfds);
  if (init(pollfds, nfds, fds, hEvents) < 0) {
    clean(nfds, fds, hEvents);
    return -1;
  }

  n = WSAWaitForMultipleEvents(nfds, hEvents, FALSE, timeout, FALSE);

  if (n == WSA_WAIT_FAILED) {
    clean(nfds, fds, hEvents);
    return -1;
  } else if (n == WSA_WAIT_TIMEOUT) {
    clean(nfds, fds, hEvents);
    return 0;
  } else {
    SOCKET fd;
    HANDLE hEvent;
    WSANETWORKEVENTS events;
    
    n -= WSA_WAIT_EVENT_0;
    fd = fds[n];
    hEvent = hEvents[n];

    if (WSAEnumNetworkEvents(fd, hEvent, &events) < 0) {
      clean(nfds, fds, hEvents);
      return -1;
    }

    pollfds[n].revents = (short) events.lNetworkEvents;
    clean(nfds, fds, hEvents);
    return n + 1;
  }
}