echod_winsock_select.c

fd_setをなめるのがめんどくさいなぁ。

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

#define _CRT_SECURE_DEPRECATE_MEMORY
#include <memory.h>

#define ECHO_PORT 7
#define MAX_BACKLOG 5
#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, WSADATA *wsaData) {
  int n;

  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", sock, buf);

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

  return len;
}


int main() {
  int i;
  WSADATA wsaData;
  SOCKET sock;
  SOCKET ss[FD_SETSIZE];
  struct sockaddr_in addr;
  fd_set fds;

  wsa_startup(MAKEWORD(2, 0), &wsaData);

  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());
  }

  for (i = 0; i < FD_SETSIZE; i++) { ss[i] = INVALID_SOCKET; }
  ss[0] = sock;

  FD_ZERO(&fds);
  FD_SET(sock, &fds);


  while(1) {
    fd_set _fds;
    struct timeval timeout;

    memcpy(&_fds, &fds, sizeof(fd_set));
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    if (select(FD_SETSIZE, &_fds, NULL, NULL, &timeout) < 0) {
      die("select(): %d\n", WSAGetLastError());
    }

    for (i = 0; i < FD_SETSIZE; i++) {
      int j;
      SOCKET fd = ss[i];

      if (fd == INVALID_SOCKET || !FD_ISSET(fd, &_fds)) { continue; }

      if (sock == fd) {
        SOCKET s;
        struct sockaddr_in caddr;
        int len = sizeof(caddr);

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

        error("accepted.\n");

        FD_SET(s, &fds);

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

        if (j >= FD_SETSIZE) {
          closesocket(s);
        }
      } else {
        if (echo(fd) < 1) {
          FD_CLR(fd, &fds);

          for (j = 0; j < FD_SETSIZE; j++) {
            if (ss[j] == fd) {
              ss[j] = INVALID_SOCKET;
              break;
            }
          }

          closesocket(fd);
        }
      }
    }
  }

  WSACleanup();
  return 0;
}