#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define BUFSIZE      8192
#define DEFAULT_PORT 5320

enum {CMD_NAME, DST_IP, DST_PORT};

int main(int argc, char *argv[])
{
  struct sockaddr_in server;  /* サーバのアドレス               */
  unsigned long dst_ip;       /* サーバのIPアドレス             */
  int port;                   /* ポート番号                     */
  int s;                      /* ソケットディスクリプタ         */
  int n;                      /* 入力データのバイト数           */
  char buf[BUFSIZE];          /* 受信バッファ                   */
  char cmd[BUFSIZE];          /* 送信バッファ                   */
  struct timeval tv;          /* selectのタイムアウト時間       */
  fd_set readfd;              /* selectで検出するディスクリプタ */

  /* 引き数のチェック */
  if (argc !=2 && argc != 3) {
    fprintf(stderr, "Usage: %s  hostname  [port]\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  /* サーバのIPアドレスを調べる */
  if ((dst_ip = inet_addr(argv[DST_IP])) == INADDR_NONE) {
    struct hostent *he;  /* ホスト情報 */

    if ((he = gethostbyname(argv[DST_IP])) == NULL) {
      fprintf(stderr, "gethostbyname error\n");
      exit(EXIT_FAILURE);
    }
    memcpy((char *)&dst_ip, (char *)he->h_addr, he->h_length);
  }

  /* サーバのポート番号を調べる */
  if (argc == 3) {
    if ((port = atoi(argv[DST_PORT])) == 0) {
      struct servent *se;     /* サービス情報 */

      if ((se = getservbyname(argv[DST_PORT], "tcp")) != NULL)
        port = (int)ntohs((u_short)se->s_port);
      else {
        fprintf(stderr, "getservbyname error\n");
        exit(EXIT_FAILURE);
      }
    }
  } else
    port = DEFAULT_PORT;

  /* TCPでソケットを開く */
  if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    exit(EXIT_FAILURE);
  }

  /* サーバのアドレスを設定しコネクションを確立する */
  memset((char *) &server, 0, sizeof(server));
  server.sin_family      = AF_INET;
  server.sin_addr.s_addr = dst_ip;
  server.sin_port        = htons(port);
  if (connect(s, (struct sockaddr *) &server, sizeof server) < 0) {
    perror("connect");
    exit(EXIT_FAILURE);
  }

  printf("connected to '%s'\n", inet_ntoa(server.sin_addr));

  /* クライアント処理メインルーチン */
  while (1) {
    /* selectのタイムアウトの設定 */
    tv.tv_sec  = 600;
    tv.tv_usec = 0;

    /* 標準入力、サーバからのメッセージがあるか */
    FD_ZERO(&readfd);
    FD_SET(0, &readfd);
    FD_SET(s, &readfd);
    if ((select(s + 1, &readfd, NULL, NULL, &tv)) <= 0) {
      fprintf(stderr, "\nTimeout\n");
      break;
    }

    /* 標準入力 */
    if (FD_ISSET(0, &readfd)) {
      if ((n = read(0, buf, BUFSIZE-1)) <= 0)
        break;
      buf[n]='\0';
      sscanf(buf, "%s", cmd);
      if (strcmp(cmd, "quit") == 0)
        break;
      if (send(s, buf, n, 0) <= 0)
        break;
    }

    /* サーバ */
    if (FD_ISSET(s, &readfd)) {
      if ((n = recv(s, buf, BUFSIZE-1, 0)) <= 0) {
        fprintf(stderr, "connection closed.\n");
        exit(EXIT_FAILURE);
      }
      buf[n]='\0';
      printf("%s", buf);
      fflush(stdout);
    }
  }

  strcpy(buf, "quit");
  send(s, buf, n, 0);
  close(s);

  return EXIT_SUCCESS;
}