다중클라이언트 코드 분석2

강한친구·2022년 3월 16일
0

Server Studies

목록 보기
17/27

이전글에서 클라이언트 받기 전까지를 알아보았으니 이제 클라이언트를 받아보는 과정을 알아보도록 하겠다.

다중 클라이언트 받기

 while(True) {
        FD_ZERO(&readfds);
        FD_SET(master_socket, &readfds);
        max_sd = master_socket;

        for (i = 0; i < max_clients; i++) {
            sd = client_socket[i];

            if (sd > 0) FD_SET(sd, &readfds);
            if (sd > max_sd) max_sd = sd;
        }

        activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);

        if ((activity < 0) && (errno != EINTR)) {
            printf({"select Error"});
        }

        if (FD_ISSET(master_socket, &readfds)) {
            if ((new_socket = accept(master_socket, (struct sockaddr*)& address, (socklen_t*)& addrlen)) < 0) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            printf("New connection , socket fd is %d , ip is : %s , port : %d\n" 
            , new_socket , inet_ntoa(address.sin_addr) , ntohs (address.sin_port));

            if (send (new_socket, message, strlen(message), 0) != strlen(message)) {
                perror("send");
            }

            puts("Welcome message sent sucessfully");

            for (i = 0; i < max_clients; i++) {
                if( client_socket[i] = 0) {
                    client_socket[i] = new_socket;
                    printf("Adding to list of socket as %d\n", i);

                    break;
                }
            }
        }

        for (i = 0; i < max_clients; i++) {
            sd = client_socket[i];

            if(FD_ISSET(sd, &readfds)) {
                if (valread = read(sd, buffer, 1024) == 0) {  
                    getpeername(sd , (struct sockaddr*)&address , \
                        (socklen_t*)&addrlen);  
                    printf("Host disconnected , ip %s , port %d \n" , 
                        inet_ntoa(address.sin_addr) , ntohs(address.sin_port));  
                    close( sd );  
                    client_socket[i] = 0;  
                } else {
                    buffer[valread] = '\0';
                    send(sd, buffer, strlen(buffer), 0);
                } 
            }
        }
    }

우선 FD_ZERO를 통해 fd_set을 비워준다.
그리고 FD_SET을 통해 fd_set에 master_socketㅇ르 집어넣는다. 그리고 max_sd를 master_socket으로 지정해주는 과정을 거친다.

그 후 select를 작동시켜서 fd_set안에 있는 통신을 모니터링한다. timeout을 설정하면 그 시간만큼 대기하고 연락이 없으면 종료된다.
그 다음부터는 두가지 경우로 나뉜다.

1. 새로운 연결 요청

if (FD_ISSET(master_socket, &readfds)) {
            if ((new_socket = accept(master_socket, (struct sockaddr*)& address, (socklen_t*)& addrlen)) < 0) {
                perror("accept");
                exit(EXIT_FAILURE);
            }
            printf("New connection , socket fd is %d , ip is : %s , port : %d\n" 
            , new_socket , inet_ntoa(address.sin_addr) , ntohs (address.sin_port));

            if (send (new_socket, message, strlen(message), 0) != strlen(message)) {
                perror("send");
            }

            puts("Welcome message sent sucessfully");

            for (i = 0; i < max_clients; i++) {
                if( client_socket[i] = 0) {
                    client_socket[i] = new_socket;
                    printf("Adding to list of socket as %d\n", i);

                    break;
                }
            }
        }

master_socket이 listen 상태일 때, 새로운 요청이 오면 이는 클라이언트의 통신요청이다. 따라서 fd_isset을 통해 master_socket이 있는지 확인할 수 있고, 그 후 accept를 통해서 fd를 열어주고 거기에 client를 저장한다. 그리고 서로간 send를 통해 메세지를 주고받는다.

그 후 socket_list에서 가장 앞에 있는 0을 가진 index에 fd값을 넣어주고 바로 break해준다.

이 fd는 다음 iteration에 readfds에 들어가서 모니터링 대상에 오르게 된다.

2. 이미 연결된 클라이언트 통신

이미 연결된 클라이언트끼리 통신은 코드만 구현되어있고 클라이언트쪽 코드가 바로 종료되도록 설정되어있어서 이를 구현해보지는 못했다.

일단 클라이언트쪽 코드들이 신호만 보내면 바로 종료되는것을 방지해야할거같다.

채팅프로그램은?

채팅프로그램은 보통
1. 쓰레드 이용
2. 다중클라이언트 이용

이 두개중에 하나로 이루어지는것 같다.
쓰레드보단 select가 나은것같다고 생각했으니 그걸로 하기로하고 원리는 어느정도 이해했는데 앞으로 해결할 문제가 몇가지 있다.

  1. ip주소를 입력받아서 처리
  2. 전달 메세지 내용을 입력받아서 처리
  3. 원하는 타이밍에 끌 수 있도록 설정

이 주요과제인것으로 생각되며,

채팅은 서버와 클라이언트의 채팅이 아니라 (이거는 기존의 server, client 코드를 이용하면 금방 구현할 수 있을것같다.)
클라이언트와 클라이언트간의 소통이 되어야한다. 따라서 A 클라이언트가 보낸 메세지를 서버가 받아서 B로 넘겨주고 반대도 수행해주는 과정을 구현하는 방법을 고민해봐야 할것같다.

지금 생각하는건
A가 보낸 메세지를 read로 받아서buffer에 넣고 buffer를 그대로 B로 보내주는 방식을 생각하고 있다.

다만 코드의 구현과, 해당 방식으로 메세지 전송시 딜레이나 loss 관련 부분을 고민해 봐야 할 것 같다.

채팅 프로그램이 완성되고 나면, 암호화통신, 사이트에 탑제할 수 있는 채팅 프로그램, 이런것들도 구현해보려고 한다.

0개의 댓글