echod_winsock_event.c

WSAWaitForMultipleEvents、資料少ない…。動くことは動いたけど、終了処理が怪しいなぁ。

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

#define _CRT_SECURE_DEPRECATE_MEMORY
#include <memory.h>

#define ECHO_PORT 7
#define MAX_BACKLOG 5
#define WSAWFME_TIMEOUT 1000
#define RCVBUFSIZE 256

#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[WSA_MAXIMUM_WAIT_EVENTS];
  HANDLE hEvents[WSA_MAXIMUM_WAIT_EVENTS];
  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));
  memset(hEvents, 0, sizeof(sockets));
  sockets[0] = sock;
  hEvents[0] = WSACreateEvent();

  if (WSAEventSelect(sock, hEvents[0], FD_ACCEPT) < 0) {
    die("WSAEventSelect(): %d\n", WSAGetLastError());
  }

  while(1) {
    DWORD n, cEvents = 0;
    SOCKET fd, _sockets[WSA_MAXIMUM_WAIT_EVENTS];
    HANDLE hEvent, _hEvents[WSA_MAXIMUM_WAIT_EVENTS];
    WSANETWORKEVENTS events;
    int j;

    for (i = 0; i < WSA_MAXIMUM_WAIT_EVENTS; i++) {
      if (hEvents[i]) {
        _sockets[cEvents] = sockets[i];
        _hEvents[cEvents] = hEvents[i];
        cEvents++;
      }
    }

    switch(n = WSAWaitForMultipleEvents(cEvents, hEvents, FALSE, WSAWFME_TIMEOUT, FALSE)) {
      case WSA_WAIT_TIMEOUT:
        continue;
      case WSA_WAIT_FAILED:
        die("WSAWaitForMultipleEvents(): %d\n", WSAGetLastError());
    }

    fd = _sockets[n - WSA_WAIT_EVENT_0];
    hEvent = _hEvents[n - WSA_WAIT_EVENT_0];

    if (WSAEnumNetworkEvents(fd, hEvent, &events) < 0) {
      die("WSAEnumNetworkEvents(): %d\n", WSAGetLastError());
    }

    if (events.lNetworkEvents & FD_ACCEPT) {
      SOCKET s;
      struct sockaddr_in caddr;
      int len = sizeof(caddr);

      if ((s = accept(fd, (struct sockaddr *) &caddr, &len)) == INVALID_SOCKET) {
        error("accept(): %d\n", WSAGetLastError());
      }

      error("accepted: %d\n", s);

      for (j = 0; j < WSA_MAXIMUM_WAIT_EVENTS; j++) {
        if (!hEvents[j]) {
          sockets[j] = s;
          hEvents[j] = WSACreateEvent();

          if (WSAEventSelect(s, hEvents[j], FD_READ | FD_CLOSE) < 0) {
            die("WSAEventSelect(): %d\n", WSAGetLastError());
          }

          break;
        }
      }

      if (j >= WSA_MAXIMUM_WAIT_EVENTS) {
        closesocket(s);
      }
    } else if (events.lNetworkEvents & FD_READ) {
      if (echo(fd) < 1) {
        for (j = 0; j < WSA_MAXIMUM_WAIT_EVENTS; j++) {
          if (sockets[j] == fd) {
            sockets[j] = INVALID_SOCKET;
            hEvents[j] = NULL;
            break;
          }
        }

        if (WSAEventSelect(fd, NULL, 0) < 0) { die("WSAEventSelect(): %d\n", WSAGetLastError()); }
        WSACloseEvent(hEvent);
        closesocket(fd);
        error("closed: %d\n", fd);
      }
    } else if (events.lNetworkEvents & FD_CLOSE) {
      for (j = 0; j < WSA_MAXIMUM_WAIT_EVENTS; j++) {
        if (sockets[j] == fd) {
          sockets[j] = INVALID_SOCKET;
          hEvents[j] = NULL;
          break;
        }
      }

      if (WSAEventSelect(fd, NULL, 0) < 0) { die("WSAEventSelect(): %d\n", WSAGetLastError()); }
      WSACloseEvent(hEvent);
      closesocket(fd);
      error("closed: %d\n", fd);
    }
  }

  if (WSAEventSelect(sock, NULL, 0) < 0) { die("WSAEventSelect(): %d\n", WSAGetLastError()); }
  WSACloseEvent(hEvents[0]);
  closesocket(sock);

  WSACleanup();
  return 0;
}