/*
 * mserver.c - Example of Multi Client Server
 *
 */

#include < stdio.h >
#include < string.h >
#include < sys/types.h >
#include < sys/socket.h >
#include < netinet/in.h > 
#include < sys/time.h >   /* selectシステムコール */
#include < unistd.h >

#define PORT 8765       /* サーバーが使うポート番号  */
#define SOCK_MAX 5      /* 最大ソケット数をここで決めた (最大32)*/
#define UNUSED (-1)

char *Modification(char *str, int length);

int main()
{
    int s[SOCK_MAX + 1]; /* array of socket descriptors */
    int max = 0;         /* max num of used socket */
    int n = 0;
    int len;
    fd_set readfds;
    int clilen;               /* client length */
    struct sockaddr_in saddr; /* サーバ側(自分)のアドレス情報 */
    struct sockaddr_in caddr; /* クライアント側のアドレス情報を入れるところ */
    char str[1024];           /* string buffer */
    int i, j;                 /* loop counter */
    int msglen;

    /* 
     *  ソケットを作る。
     *  このソケットはINETドメインでストリーム型(コネクション型) 。
     */
    if ((s[0] = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
	perror("socket");
	exit(1);
    }

    /* 
     * saddrの中身を0にしておかないと、bind()でエラーが起こることがある
     */
    bzero((char *)&saddr, sizeof(saddr));

    /* ソケットに名前をつける bind() */
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(PORT); /* 定数PORTは#defineしたもの */

    if ((bind(s[0], (struct sockaddr *)&saddr, sizeof(saddr))) == -1) {
	perror("bind");
	exit(1);
    }

    /* 接続されるのを待つ listen() */
    /* 第2引数の値を大きくする */
    if ((listen(s[0], SOCK_MAX)) == -1) {
	perror("listen");
	exit(1);
    } 
    max = 1;   /* s[0] is ready */

    /* メインループ */
    while (1) {
	FD_ZERO(&readfds);    /* fdsの初期化 */
	printf("max: %d\n", max);
	/* 
	 * すでにクライアントと接続されているソケットのビットを
	 * セットしてselect()の検査対象にする。
	 */
	for (i = 0; i < max; i++) {
	    if (s[i] != UNUSED) {
		FD_SET(s[i], &readfds);
	    }
	}
	/* 
	 * メッセージが到着しているソケットがないか調べる
	 */
	if ((n = select(FD_SETSIZE, &readfds, NULL, NULL, NULL)) == -1) {
	    perror("select");
	    exit(1);
	}
	printf("select returns: %d\n", n);
	/*   
	 *  1〜maxのソケットに届くのは、すでに接続されているクライアントからの
	 *  メッセージである。
	 *  もしメッセージがあれば、接続されているすべてのクライアントに送る。
	 */
	for (i = 1; i < max; i++) {
	    if (s[i] != UNUSED) {
		if (FD_ISSET(s[i], &readfds)) {
		  /* 
		   * s[i]のビットが立っていれば、s[i]にメッセージが到着している
		   */
		    printf("s[%d] ready for reading\n", i);
		    if ((msglen = read(s[i], str, sizeof(str))) == -1) {
			/* 受信失敗 */
			perror("read");
		    } else if (msglen != 0) {
			/* メッセージの受信に成功 */
			printf("client[%d]: %s", i, str);
			Modification(str,msglen);   /* 作業 */
		      /* 接続されているクライアントすべてにメッセージを送る */
			for (j = 1; j < max; j++) {
			    if (s[j] != UNUSED) {
				write(s[j], str, strlen(str));
			    }
			}
		    } else {
			printf("client[%d]: connection closed.\n", i);
			close(s[i]);
			s[i] = UNUSED;
		    }
		}
	    }
	}	
	/* 
	 *  新たな接続要求があった場合は、s[0]のビットが立つ。
	 *  以下では新たな接続の受け付けを行う。
	 */
	if (FD_ISSET(s[0], &readfds) != 0) {
	    printf("Accept New one.\n");
	    /* 接続を確立する accept() */
	    len = sizeof(caddr);
	    s[max] = accept(s[0], (struct sockaddr *)&caddr, &len);
	    printf("%d = accept()\n", s[max]);
	    if (s[max] == -1) {
		perror(NULL);
		exit(1);
	    }
	    if (max < SOCK_MAX) {
		printf("client accepted(%d).\n", max);
		max++;
	    } else {
		printf("refuse connection.\n");
		strcpy(str, "Server is too busy.\n");
		write(s[max], str, strlen(str));
		close(s[max]);
	    }
	}
    }
}

/* 大文字に変換する */
char *Modification(char *str, int length) {
  int i;
  for (i = 0; i < length; i++) {
    if (isalpha(str[i])) {
      str[i] = toupper(str[i]);
    }
  }

  return str;
}