/*

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

LINUX,SERVER,SELECT(RECV),SELECT(SEND)

*/

 

#include <stdio.h>

#include <fcntl.h>

#include <stdlib.h>

#include <signal.h>

#include <sys/socket.h>

#include <sys/file.h>

#include <netinet/in.h>

#include <string.h>

 

#define MAXLINE 512

#define MAX_SOCK 64

 

char *escapechar = "exit";

 

int maxfdp1;

int g_num_chat = 0;

int s;

int client_s[MAX_SOCK];

 

static int getmax(int);

static void removeclient(int);

static int getdottedipaddr(int sd, struct sockaddr_in *addr);

static void my_signal(int signo);

 

int main(int argc, char *argv[]) 

{

    char rline[MAXLINE+1];

    char *start = "Connected to chat-server\n";

    int i, j, n;

    int client_fd, clilen;

 

    fd_set read_fds;

    struct sockaddr_in client_addr, server_addr;

 

    if (argc != 2) {

        printf("usage : %s port\n", argv[0]);

        return(-1);

    }

 

    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {

        printf("can't open stream socket.");

        return(-2);

    }

 

    bzero((char *)&server_addr, sizeof(server_addr));

    server_addr.sin_family = AF_INET;

    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    server_addr.sin_port = htons(atoi(argv[1]));

 

    if (bind(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {

        printf("Server: Can't bind local address.\n");

        return(-3);

    }

 

    if (signal(SIGINT, my_signal) == SIG_ERR) { /* Ctrl+C */

        printf("Server: signal(SIGINT) error\n");

        return(-4);

    }

    if (signal(SIGTERM, my_signal) == SIG_ERR) { /* software termination */

        printf("Server: signal(SIGTERM) error\n");

        return(-5);

    }

    if (signal(SIGQUIT, my_signal) == SIG_ERR) { /* Ctrl+\ */

        printf("Server: signal(SIGQUIT) error\n");

        return(-6);

    }

 

 

    listen(s, 5); /* backlog = 5 */

 

    maxfdp1 = s + 1;

 

    while (1) 

    {

        FD_ZERO(&read_fds);

        FD_SET(s, &read_fds);

 

        for (ii=0; ii < g_num_chat; ii++)

        {

            FD_SET(client_s[ii], &read_fds);

}

        maxfdp1 = getmax(s) + 1; /* maxfdp1 재 계산 */

 

        /*wait & response*/

/*wait & response*/

/*wait & response*/

/*wait & response*/

/*wait & response*/

/*wait & response*/

/*wait & response*/

/*wait & response*/

        if (select(maxfdp1, &read_fds, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0) 

        {

            printf("select error.\n");

            break;

        }

 

        if (FD_ISSET(s, &read_fds)) 

        {

            clilen = sizeof(client_addr);

            client_fd = accept(s, (struct sockaddr *)&client_addr, &clilen);

            if (client_fd == -1) 

            {

                printf("server: accept error\n");

                break;

            }

 

            client_s[g_num_chat] = client_fd;

            g_num_chat++;

 

            /*welcome message*/

/*welcome message*/

/*welcome message*/

/*welcome message*/

/*welcome message*/

/*welcome message*/

/*welcome message*/

/*welcome message*/

            send(client_fd, start, strlen(start), 0);

 

            if (getdottedipaddr(client_fd, &client_addr) > -1)

                printf("%d number additional(IP: %s)\n", g_num_chat, inet_ntoa(client_addr.sin_addr));

            else

                printf("%d number additional\n", g_num_chat);

        }

        for (ii=0; ii<g_num_chat; ii++) 

        {

            if (FD_ISSET(client_s[ii], &read_fds)) 

            {

                if ((n = recv(client_s[ii], rline, MAXLINE, 0)) <= 0) 

                {

                    removeclient(ii); /* abrupt exit *//* abrupt exit *//* abrupt exit *//* abrupt exit */

                    continue;

                }

                rline[n] = '\0';

 

                if (strstr(rline, escapechar) != NULL) 

                {

                    removeclient(ii); /* abrupt exit *//* abrupt exit *//* abrupt exit *//* abrupt exit *//* abrupt exit */

                    continue;

                }

 

                for (kk=0; kk<g_num_chat; kk++)

                {

                    send(client_s[kk], rline, n, 0);

                }

 

                if (getdottedipaddr(client_fd, &client_addr) > -1)

                printf("IP: %s -> %s", inet_ntoa(client_addr.sin_addr), rline);

                else

                printf("%s", rline);

            }

        }

    } /* while */

    my_signal(SIGIO);

}

 

void removeclient(int indx) 

{

    struct sockaddr_in client_addr;

 

    if (getdottedipaddr(client_s[indx], &client_addr) > -1)

    printf("1 number expired!!(IP: %s).", inet_ntoa(client_addr.sin_addr));

    else

    printf("1 number expired!!.");

 

    close(client_s[indx]);

 

    if (indx != g_num_chat-1) client_s[indx] = client_s[g_num_chat-1];

 

    g_num_chat--;

    printf("now number = %d\n", g_num_chat);

}

 

int getmax(int k) {

 

    int max = k;

    int r;

 

    for (r=0; r < g_num_chat; r++)

    {

        if (client_s[r] > max) max = client_s[r];

    }

    return max;

}

 

/* 소켓에 연결된 상대방 주소를 알아낸다 */

int getdottedipaddr(int sd, struct sockaddr_in *addr) {

    struct sockaddr_in client_addr;

    int len, r;

 

    len = sizeof(client_addr);

    if ((r = getpeername(sd, (struct sockaddr *)&client_addr, &len)) == 0)

    {

        *addr = client_addr;

    }

    return r;

}

 

/* 시그널 처리 함수 정의 */

void my_signal(int signo) 

{

    int ii;

 

/* 대부분의 시그널(SIGILL과 SIGTRAP 은 제외)에 대한 처리 함수는 시그널이 포작된 후

즉시 재지정된다. 이것은 프로세스가 시그널의 처리 함수를 잊어버리고, 다음에 시그널이

도착하면 묵시적 처리함수인 SIG_DFL 을 수행하는 것을 의미한다.

이것은 특히 사용자가 인터럽트키를 여러번 누를 수도 있는 대화명 프로그램에서 문제가 된다

그러므로 시그널 함수가 호출되자 마자 SIGINT 시그널을 무시한다 */

    signal(SIGINT, SIG_IGN);

    signal(SIGTERM, SIG_IGN);

    signal(SIGQUIT, SIG_IGN);

 

    close(s);

    for (ii=0; ii < g_num_chat; ii++)

    {

        close(client_s[ii]);

    }

    exit(0);

}

/*----------------------------------------------------------------------------------------------------

socket function : accept

 

1. 소켓에 연결을 받아들인다.

#include <sys/types.h>

#include <sys/socket.h>

 

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

 

2. 설명

accept() 함수는 연결지향 소켓 타입 (SOCK_STREAM, SOCK_SEQPACKET, SOCK_RDM)에 사용된다. 

이것은 아직 처리되지 않은 연결들이 대기하고 있는 큐에서 제일 처음 연결된 연결을 가져와서 새로운 연결된 소켓을 만든다. 

그리고 소켓을 가르키는 파일 지정자를 할당하고 이것을 리턴한다.

인자 s 는 socket() 로 만들어진 end-point(듣기 소켓)을 위한 파일지정자이다.

 

인자 addr 는 sockaddr 구조체에 대한 포인터이다. 

연결이 성공되면 이 구조체를 채워서 되돌려 주게 되고, 

우리는 이구조체의 정보를 이용해서 연결된 클라이언트의 인터넷 정보를 알아낼수 있다. addrlen 인자는 addr의 크기 이다.

 

만약 미결인 연결이 큐에 존재하지 않고, 소켓이 비봉쇄가 아니라면 accept 는 연결이 존재할때까지 해당영역에서 봉쇄된다. 

비봉쇄 소켓일경우에는 errno 로 EAGAIN 을 설정하고 바로 리턴한다.

 

3. 반환값

에러시 -1이 반환된다. 성공한다면 받아들인 소켓을 위한 파일지정번호 (0보다 큰)을 반환한다.

---------------------------------*/

+ Recent posts