본문 바로가기

BackEnd/Linux

Linux / network - 리눅스기초를 활용한 데이터 통신 6(서버, 클라이언트, 소켓통신, 데이터 합치기)

안녕하세요 인포돈입니다.

본 내용은 우분투를 기본으로 작성되었습니다.
Cloud Computing을 활용하여 서버를 구축하였습니다.

 

3개의 클라이언트 데이터 최소, 최대, 평균값 통합하기

 이번 코딩은 3개의 클라이언트에서 각 숫자 데이터를 보내오면 서버 측에서는 최댓값, 최솟값, 평균값을 통합하여 각 클라이언트에 보내줍니다. 이때 최대, 최소의 경우 어디 ip와 port에서 보내오는지 출력이 되게 합니다.

 

서버 코드

struct info{
     char sin_addr[MAXBUF];
     int sin_port;
     int value;
};
struct mmv{
     char max_addr[MAXBUF], min_addr[MAXBUF];
     int max_port, min_port;
     int max, min;
     float avg;
};

int main(int argc, char **argv){
     int server_sockfd, client_sockfd;
     int client_temp[3];
     int client_len, n;
     char buf[MAXBUF];
     struct sockaddr_in clientaddr, serveraddr;
     client_len = sizeof(clientaddr);
     int min, max, sum;
     float avg;
     struct info temp[3];
     struct mmv msg;
     
     if ((server_sockfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP )) == -1) {
     perror("socket error : ");
     exit(0);
     }
     
     memset(&serveraddr, 0x00, sizeof(serveraddr));
     serveraddr.sin_family = AF_INET;
     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
     serveraddr.sin_port = htons(atoi(argv[1]));
     
     bind (server_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) ;
     listen(server_sockfd, 5);
     
     while(1){
     for(int i = 0 ; i < 3 ; i++){
         client_temp[i] = accept(server_sockfd, (struct sockaddr *)&clien
        taddr,&client_len);
         printf("New Client[%d] Connect: (IP) %s : (PORT) %d\n",i, inet_n
        toa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
         strcpy(temp[i].sin_addr,inet_ntoa(clientaddr.sin_addr));
         temp[i].sin_port = ntohs(clientaddr.sin_port);
     }
     memset(buf, 0x00, MAXBUF);
     
     for(int i = 0 ; i < 3 ; i ++){
         if ((n = read(client_temp[i], buf, MAXBUF)) <= 0){
             close(client_sockfd);
             continue;
             }
         printf("from client[%d] : %s",i,buf);
         for(int i = 0 ; buf[i]!=0 ; i++){
             if(buf[i]=='\n'){
             buf[i]=0;
             break;
             }
         }
     temp[i].value = atoi(buf);
     }
     
     max = temp[0].value;
     min = temp[0].value;
     
     for(int i = 0 ; i < 3 ; i++){
         sum += temp[i].value;
         max = (max < temp[i].value ? temp[i].value : max);
         min = (min > temp[i].value ? temp[i].value : min);
         printf("temp[%d] = %d \n",i,temp[i].value);
         }
         
     avg = sum/3;
     for(int i = 0 ; i < 3 ; i ++){
         if(temp[i].value == min){
             strcpy(msg.min_addr,temp[i].sin_addr);
             msg.min_port = temp[i].sin_port;
             msg.min = temp[i].value;
             printf("%d : %s : % d \n",msg.min, msg.min_addr, msg.min_port);
         }
     if(temp[i].value== max){
         printf("max : %d from %s:%d \n",temp[i].value,temp[i].sin_addr,
        temp[i].sin_port);
         strcpy(msg.max_addr,temp[i].sin_addr);
         msg.max_port = temp[i].sin_port;
         msg.max = temp[i].value;
         }
     }
     
     printf("avg : %f \n",avg);
     msg.avg = avg;
     
     for(int i = 0 ; i < 3 ; i++){
         if (write(client_temp[i], (char *)&msg, sizeof(msg)) <= 0) {
             perror("write error : ");
             close(client_sockfd);
             }
         close(client_temp[i]);
         }
     }
     close(server_sockfd);
     return 0;
}

 서버 측 코드를 면밀히 분석해볼 수 있다. 앞선 소켓의 기본적인 생성에서부터 대기까지의 코드는 생략하겠습니다. 이전 글을 확인해보시면 됩니다.

 

Linux / network - 리눅스기초를 활용한 데이터 통신 5(서버, 클라이언트, 소켓통신, 데이터 합치기)

안녕하세요 인포돈입니다. 이번 포스팅부터 본격적인 리눅스 소켓 통신 코딩을 적어보겠습니다. 본 포스터에서는 기본적으로 널리 알려져 있는 기본 코드를 활용하여 클라이언트의 데이터를

infodon.tistory.com

 

 - 구조체 info와 mmv

struct info{
	char sin_addr[MAXBUF];
    int sin_port;
    int valuel
}

struct mmv{
	char max_addr [MAXBUF], min_addr[MAXBUF];
    int max_port, min_port;
    int max, min;
    float avg;
}

info 구조체의 경우 client로 받은 ip정보, port정보, value정보를 저장하기 위한 구조체입니다. mmv 구조체의 경우 client로 보낼 구조체로 max, min, avg에 관한 정보를 저장하기 위한 구조체입니다. (해당 구조체를 client에서 사용하려면 똑같은 type, 순서, 변수를 통일하여 선언해주어야 합니다.)

 

 - client로부터 들어온 정보 저장

         strcpy(temp[i].sin_addr,inet_ntoa(clientaddr.sin_addr));
         temp[i].sin_port = ntohs(clientaddr.sin_port);

각 클라이언트로부터 들어온 정보를 info구조체인 temp배열에 저장해 줍니다. 이때 클라이언트에 들어온 값은 아직 read하지 않았기에 기본적으로 ip와 port번호만 저장해 둡니다.

temp[i].value = atoi(buf)

해당 코를 활용해서 각 클라이언트에 들어온 값을 temp배열에 저장해줍니다.

(그 이전 코드는 이전 포스팅에서도 설명했듯이 각 클라이언트에서 read를 통해 어떠한 값을 받은 후 받은 값의 마지막에 배열의 끝을 알려주는 0을 넣어주어 필요 없는 배열의 뒷부분을 생략하게 하는 코드입니다.)

 

 - 받은 값들 최대, 최소, 평균값 찾기

     max = temp[0].value;
     min = temp[0].value;
     
     for(int i = 0 ; i < 3 ; i++){
         sum += temp[i].value;
         max = (max < temp[i].value ? temp[i].value : max);
         min = (min > temp[i].value ? temp[i].value : min);
         printf("temp[%d] = %d \n",i,temp[i].value);
         }
         
     avg = sum/3;

앞선 코드를 모두 에러 없이 진행하였다면, 이제 temp에는 각 클라이언트의 ip, port, value의 값들이 지정되어 있습니다. 그러면 우리는 이제 그 값 중 최대, 최소 값이 무엇인지를 파악합니다. 또한 순회를 돌면서 평균을 구해주는 코드입니다.

 

 - 클라이언트에 보낼 mmv구조체 초기화하기

     for(int i = 0 ; i < 3 ; i ++){
         if(temp[i].value == min){
             strcpy(msg.min_addr,temp[i].sin_addr);
             msg.min_port = temp[i].sin_port;
             msg.min = temp[i].value;
             printf("%d : %s : % d \n",msg.min, msg.min_addr, msg.min_port);
         }
     if(temp[i].value== max){
         printf("max : %d from %s:%d \n",temp[i].value,temp[i].sin_addr,
        temp[i].sin_port);
         strcpy(msg.max_addr,temp[i].sin_addr);
         msg.max_port = temp[i].sin_port;
         msg.max = temp[i].value;
         }
     }
     
     printf("avg : %f \n",avg);
     msg.avg = avg;

 사실 코드가 굉장히 길어 보이지만, temp을 순회하면서 이전에 찾은 최대, 최소와 값이 같다면, 해당 값에 맞는 ip와 port번호를 mmv구조체인 msg에 그 값들을 넣어줍니다. 


클라이언트 코드

struct mmv{
     char max_addr[MAXLINE], min_addr[MAXLINE];
     int max_port, min_port;
     int max, min;
     float avg;
};

int main(int argc, char **argv){
     struct sockaddr_in serveraddr;
     int server_sockfd;
     int client_len;
     int rbyte;
     char buf[MAXLINE];
     struct mmv mdata;
     
     if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
         perror("error :");
         return 1;
         }
         
     server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
     serveraddr.sin_family = AF_INET;
     serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
     serveraddr.sin_port = htons(3600);
     client_len = sizeof(serveraddr);
     
     if (connect(server_sockfd, (struct sockaddr *)&serveraddr, client_len) == -1){
         perror("connect error :");
         return 1;
         }
     
     memset(buf, 0x00, MAXLINE);
     read(0, buf, MAXLINE);
     
     if (write(server_sockfd, buf, MAXLINE) <= 0){
         perror("write error : ");
         return 1;
         }
     memset(buf, 0x00, MAXLINE);
     
     rbyte = read(server_sockfd, (char *)&mdata, sizeof(mdata));
     
     printf("min=%d from %s : %d\n", mdata.min, mdata.min_addr, mdata.min_port);
     printf("max=%d from %s : %d\n", mdata.max, mdata.max_addr, mdata.max_port);
     printf("avg = %.1f\n", mdata.avg);
     close(server_sockfd);
     return 0;
}

사실 클라이언트의 코드는 그리 리뷰할 게 없습니다. 기본적으로 일반 소켓 시스템 클라이언트 코드와 비슷하지만, 이전에 server 측에서 정의했던 mmv구조체만 따로 선언해주어 서버에서 read 한 값을 그대로 print만 해주면 됩니다. 이때 반듯이 서버 측과 구조체의 순서, 변수명, type이 똑같아야 합니다.


결과 화면

서버 실행 화면
클라이언트 실행 화면


이번에 소켓을 만지는 코딩을 하면서 지나치게 쓸데없는 코드들이 많이 있음을 느껴볼 수 있었습니다. 단순 최대, 최소 값을 찾는 코딩이었지만, 서버에서 클라이언트로 데이터를 보내면서, 이해하지 못하였던 오류들이 많이 나와서 고치다 보니 이러한 코딩을 하게 되었던 거 같습니다. 결론적으로 리팩터링 작업을 더욱 거쳐 최적의 코드를 만들어 봐야 한다. 이후에는 이점에 좀 더 중점을 두어 포스팅을 하려 합니다.