echoサーバを書いてみた: 例外を使う

さすがに「これはひどい」だったので、例外を使うように書き直す。
テンプレート、便利だなー。

#include <string>
#include <iostream>
#include <sstream> 

#include <winsock2.h>

namespace wsa {
  int cleanup() {
    return WSACleanup();
  }

  void startup(WORD version) {
    WSADATA wsa_data;
    int n;

    if ((n = WSAStartup(version, &wsa_data)) != 0) {
      std::cerr << "WASStartup(): " << n << "\n";
      cleanup();
      exit(1);
    }

    if (version != wsa_data.wVersion) {
      std::cerr << "WASStartup(): WinSock version " << LOWORD(version) << "." << HIWORD(version) << " not supported\n";
      cleanup();
      exit(1);
    }
  }
}

namespace echod {
  class Exception {
    std::stringstream msgbuf;
  public:
    Exception();
    Exception(Exception &e);
    std::string message();
    template<class T> Exception &operator<<(T x);
  };

  Exception::Exception() {
  }

  Exception::Exception(const Exception &e) {
    //msgbuf << e.message();
    msgbuf << e.msgbuf.str();
  }

  std::string Exception::message() {
    return msgbuf.str();
  }

  template<class T> Exception &Exception::operator<<(T x) {
    msgbuf << x;
    return *this;
  }

  class Server {
    static const int BACKLOG = 5;
    static const int RCVBUFSIZE = 256;

    int port;
    SOCKET socket;
    sockaddr_in addr;
    void echo(SOCKET &s);
  public:
    Server(int port = 7);
    void create_socket();
    void bind();
    void listen();
    void start();
  };

  void Server::echo(SOCKET &s) {
    while (true) {
      char buf[RCVBUFSIZE];
      size_t len = ::recv(s, buf, RCVBUFSIZE, 0);

      if (len < 0) {
        throw Exception() << "recv(): " << WSAGetLastError();
      }

      if (len == 0) {
        break;
      }

      std::cout << std::string(buf, len) /*<< "\n"*/;

      if (::send(s, buf, len, 0) < 0) {
        throw Exception() << "send(): " << WSAGetLastError();
      }
    }
  }

  Server::Server(int port) : port(port) {
  }

  void Server::create_socket() {
    socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (socket == INVALID_SOCKET) {
      throw Exception() << "socket(): " << WSAGetLastError();
    }

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

  void Server::bind() {
    if (::bind(socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) != 0) {
      throw Exception() << "bind(): " << WSAGetLastError();
    }
  }

  void Server::listen() {
    if (::listen(socket, BACKLOG) != 0) {
      throw Exception() << "listen(): " << WSAGetLastError();
    }
  }

  void Server::start() {
    while(true) {
      sockaddr_in caddr;
      int len = sizeof(caddr);

      std::cerr << "wait...";

      SOCKET s = accept(socket, reinterpret_cast<sockaddr *>(&caddr), &len);

      if (s == INVALID_SOCKET) {
        throw Exception() << "accept(): " << WSAGetLastError();
      }

      std::cerr << "accepted.\n";

      echo(s);
      closesocket(s);
    }
  }
}

int main() {
  int code = 0;
  wsa::startup(MAKEWORD(2, 0));

  try {
    echod::Server echod;
    echod.create_socket();
    echod.bind();
    echod.listen();
    echod.start();
  } catch (echod::Exception &e) {
    std::cerr << e.message() << "\n";
  }

  wsa::cleanup();

  return code;
}

コピーコンストラクタの引数をconstにできない…なーぜー。

「msgbuf << e.message()」を「msgbuf << e.msgbuf.str()」にしたら、constにできた。
コピーコンストラクタの中でthisを参照できない?(コピー元なのに?)


あとException#<<(T &x)とかできない。なーぜー。