티스토리 뷰

이번 글에서는 서로 어떻게 데이터를 주고 받았는 지를 서술할 것이다.

 

마스터 서버 


        transfer_header_t transfer_header;
        memset(&transfer_header, 0, sizeof(transfer_header_t));

        // 파일 리스트 직렬화
        update_header_set(file_list, &transfer_header, 1);
        file_list_serialized(&serialized_data, &transfer_header, file_list);

        // 직렬화 데이터 압축화
        if (transfer_header.total_size > COMPRESS_BOUNDARY)
        {
            transfer_header.total_size = serialized_data_compress(&serialized_data, &transfer_header, transfer_header.total_size);
        }

        // 파일 리스트 직렬화 데이터 전송
        send(socket_fd, serialized_data, sizeof(transfer_header_t) + transfer_header.total_size, 0);

        // 사용한 직렬화 데이터 해제
        if (NULL != serialized_data)
        {
            free(serialized_data);
            serialized_data = NULL;
        }

        // 업데이트 역직렬화 수신
        memset(&transfer_header, 0, sizeof(transfer_header_t));

        // 업데이트 리스트의 헤더 수신
        recv(socket_fd, &transfer_header, sizeof(transfer_header_t), 0);

        // 수신 받을 직렬화 데이터 할당
        serialized_data = (unsigned char *)malloc(transfer_header.total_size);

        // 업데이트 리스트의 직렬화 데이터 수신
        recv(socket_fd, serialized_data, transfer_header.total_size, 0);

        // 압축 데이터의 경우 압축 해제
        if (transfer_header.data_type > COMPRESS_TYPE)
        {
            serialized_data_decompress(&serialized_data, &transfer_header);
        }
        
        // 업데이트 리스트의 직렬화 데이터 역작렬화
        file_path_deserialized(file_list, &serialized_data, transfer_header.file_count);
        
        if (NULL != serialized_data)
        {
            free(serialized_data);
            serialized_data = NULL;
        }

        // 파일 직렬화 전송
        //  업데이트 리스트가 존재할 경우에만 실행
        if (transfer_header.file_count > 0)
        {
            update_header_set(file_list, &transfer_header, 3);
            file_serialized(&serialized_data, file_list, transfer_header);

            // 직렬화 데이터 압축화
            if (transfer_header.total_size > COMPRESS_BOUNDARY)
            {
                transfer_header.total_size = serialized_data_compress(&serialized_data, &transfer_header, transfer_header.total_size);
            }
            
            send(socket_fd, serialized_data, sizeof(transfer_header_t) + transfer_header.total_size, 0);
          
          if (NULL != serialized_data)
            {
                free(serialized_data);
                serialized_data = NULL;
            }
        }

다음 마스터 서버에서 슬레이브 서버에게 데이터를 전송하는 코드이다.

길지만 3파트로 분리할 수 있다.

 

파일 리스트를 직렬화하여 슬레이브 서버에 전송하는 파트.

        // 파일 리스트 직렬화
        update_header_set(file_list, &transfer_header, 1);
        file_list_serialized(&serialized_data, &transfer_header, file_list);

        // 직렬화 데이터 압축화
        if (transfer_header.total_size > COMPRESS_BOUNDARY)
        {
            transfer_header.total_size = serialized_data_compress(&serialized_data, &transfer_header, transfer_header.total_size);
        }

        // 파일 리스트 직렬화 데이터 전송
        send(socket_fd, serialized_data, sizeof(transfer_header_t) + transfer_header.total_size, 0);

 

 

슬레이브 서버에서 받은 업데이트 경로들을 전송하는 파트.

	    // 업데이트 리스트의 헤더 수신
        recv(socket_fd, &transfer_header, sizeof(transfer_header_t), 0);

        // 수신 받을 직렬화 데이터 할당
        serialized_data = (unsigned char *)malloc(transfer_header.total_size);

        // 업데이트 리스트의 직렬화 데이터 수신
        recv(socket_fd, serialized_data, transfer_header.total_size, 0);

        // 압축 데이터의 경우 압축 해제
        if (transfer_header.data_type > COMPRESS_TYPE)
        {
            serialized_data_decompress(&serialized_data, &transfer_header);
        }
        
        // 업데이트 리스트의 직렬화 데이터 역작렬화
        file_path_deserialized(file_list, &serialized_data, transfer_header.file_count);

 

업데이트 경로에 있는 파일들을 직렬화하여 전송하는 파트이다.

        // 파일 직렬화 전송
        //  업데이트 리스트가 존재할 경우에만 실행
        if (transfer_header.file_count > 0)
        {
            update_header_set(file_list, &transfer_header, 3);
            file_serialized(&serialized_data, file_list, transfer_header);

            // 직렬화 데이터 압축화
            if (transfer_header.total_size > COMPRESS_BOUNDARY)
            {
                transfer_header.total_size = serialized_data_compress(&serialized_data, &transfer_header, transfer_header.total_size);
            }
            
            send(socket_fd, serialized_data, sizeof(transfer_header_t) + transfer_header.total_size, 0);
        }

 

 

로직을 간단히 설명하면 

  1. 마스터 서버에서 파일 리스트를 슬레이브에 전송한다. 이때 체크썸도 함께 보낸다.
  2. 슬레이브에서 파일 리스트를 수신하고, 슬레이브의 파일리스트와 체크썸을 서로 비교한다.
  3. 그리고, 슬레이브 파일리스트의 없는 경로거나 체크썸이 다른 경로들을 마스터 서버에 전송한다.
  4. 마스터 서버는 이를 수신하고, 업데이트가 필요한 파일들의 데이터를 전송한다.

 

모든 전송에서 직렬화 역직렬화, 그리고 압축화 로직이 포함되어 있다.

각 직렬화 데이터 구성은 이전 글을 참고.

2024.02.20 - [프로젝트] - 프로젝트: 파일 동기화 - 데이터 직렬화

 

프로젝트: 파일 동기화 - 데이터 직렬화

웹 사이트에서 서버와 통신을 진행할 때, 데이터를 json타입으로 전송한다. 이렇게 변환하는 이유는 다양한 타입의 데이터를 하나로 통일하여, 편의성을 높이기 위해서이다. 만약 이러한 과정이

lhs9602.tistory.com

 

 

슬레이브 서버


    // 마스터 서버와의 연결이 없으면 종료. 기존의 변경사항 체크로 넘어감
    while (TRUE)
    {

        transfer_header_t transfer_header;
        memset(&transfer_header, 0, sizeof(transfer_header_t));

        // 파일 리스트의 헤더 수신
        recv(master_server_socket, &transfer_header, sizeof(transfer_header_t), 0);

        if (NULL != serialized_data)
        {
            free(serialized_data);
            serialized_data = NULL;
        }
        serialized_data = (unsigned char *)malloc(transfer_header.total_size);

        recv(master_server_socket, serialized_data, transfer_header.total_size, 0);

        if (transfer_header.data_type > COMPRESS_TYPE)
        {
            serialized_data_decompress(&serialized_data, &transfer_header);
            transfer_header.data_type -= COMPRESS_TYPE;
        }
        // 파일 리스트 역직렬화 수신
        if (1 == transfer_header.data_type)
        {
            file_list_deserialized(&serialized_data, file_list, transfer_header.file_count, sync_file_path);
            if (NULL != serialized_data)
            {
                free(serialized_data);
                serialized_data = NULL;
            }
            update_header_set(file_list, &transfer_header, 2);

            // 전송할 파일 경로가 없으면 종료
            if (0 == transfer_header.file_count)
            {
                printf("전송할 파일 경로가 없습니다.\n");
                break;
            }

            file_path_serialized(&serialized_data, &transfer_header, file_list);

            // 직렬화 데이터 압축화
            if (transfer_header.total_size > COMPRESS_BOUNDARY)
            {
                transfer_header.total_size = serialized_data_compress(&serialized_data, &transfer_header, transfer_header.total_size);
            }

            send(master_server_socket, serialized_data, sizeof(transfer_header_t) + transfer_header.total_size, 0);
        }
        
        // 파일 역직렬화 수신
        else if (3 == transfer_header.data_type)
        {

            file_deserialized(&serialized_data, transfer_header.file_count, NULL);
            break;
        }
    }

슬레이브 서버도 3파트로 분류가 가능하다.

 

헤더와 데이터를 받고, 압축을 해제하는 파트

        transfer_header_t transfer_header;
        memset(&transfer_header, 0, sizeof(transfer_header_t));

        // 파일 리스트의 헤더 수신
        recv(master_server_socket, &transfer_header, sizeof(transfer_header_t), 0);

        if (NULL != serialized_data)
        {
            free(serialized_data);
            serialized_data = NULL;
        }
        serialized_data = (unsigned char *)malloc(transfer_header.total_size);

        recv(master_server_socket, serialized_data, transfer_header.total_size, 0);

        if (transfer_header.data_type > COMPRESS_TYPE)
        {
            serialized_data_decompress(&serialized_data, &transfer_header);
            transfer_header.data_type -= COMPRESS_TYPE;
        }

슬레이브 서버는 총 2번의 데이터 수신을 받는다.

2개는 다른 구조를 가진 데이터지만 헤더와 직렬화 데이터, 압축해제 부분은 동일하기에 로직을 공유한다.

 

파일 리스트를 역직렬화하는 파트이다.

        // 파일 리스트 역직렬화 수신
        if (1 == transfer_header.data_type)
        {
            file_list_deserialized(&serialized_data, file_list, transfer_header.file_count, sync_file_path);
            if (NULL != serialized_data)
            {
                free(serialized_data);
                serialized_data = NULL;
            }
            update_header_set(file_list, &transfer_header, 2);

            // 전송할 파일 경로가 없으면 종료
            if (0 == transfer_header.file_count)
            {
                printf("전송할 파일 경로가 없습니다.\n");
                break;
            }

            file_path_serialized(&serialized_data, &transfer_header, file_list);

            // 직렬화 데이터 압축화
            if (transfer_header.total_size > COMPRESS_BOUNDARY)
            {
                transfer_header.total_size = serialized_data_compress(&serialized_data, &transfer_header, transfer_header.total_size);
            }

            send(master_server_socket, serialized_data, sizeof(transfer_header_t) + transfer_header.total_size, 0);
        }

수신한 헤더의 데이터 타입 1이면 파일 리스트를 전송한 것이기에 파일 리스트를 역직렬화하고, 업데이트 경로를 마스터 서버에 수신한다. 

 

파일 데이터를 역직렬화하는 코드이다.

        // 파일 역직렬화 수신
        else if (3 == transfer_header.data_type)
        {

            file_deserialized(&serialized_data, transfer_header.file_count, NULL);
            break;
        }

데이터 타입이 3이면 파일 데이터이고, 역직렬화하여 파일을 저장한다.

그 후, 슬레이브 서버의 로직을 종료한다.

 

 

파일 리스트 직렬화,역직렬화


void file_list_serialized(unsigned char **serialized_data, transfer_header_t *transfer_header, file_list_t *file_list)
{
    if (NULL == file_list)
    {
        printf("file_list_serialized 매개변수가 올바르지 않습니다.\n");
        return;
    }

    // 직렬화 데이터 동적 할당
    *serialized_data = (unsigned char *)malloc(sizeof(transfer_header_t) + transfer_header->total_size);

    // 직렬화 데이터의 포인터 설정
    unsigned char *serialized_data_ptr = *serialized_data;

    // 헤더 저장 및 포인터 이동
    memcpy(serialized_data_ptr, transfer_header, sizeof(transfer_header_t));
    serialized_data_ptr += sizeof(transfer_header_t);

    file_list_t *current_file_data = NULL;
    file_list_t *tmp = NULL;

    HASH_ITER(hh, file_list, current_file_data, tmp)
    {
        memcpy(serialized_data_ptr, &current_file_data->file_path_size, sizeof(unsigned long));
        serialized_data_ptr += sizeof(unsigned long);

        snprintf(serialized_data_ptr, current_file_data->file_path_size, "%s", current_file_data->path);
        serialized_data_ptr += current_file_data->file_path_size;

        memcpy(serialized_data_ptr, current_file_data->check_sum, SHA256_DIGEST_LENGTH);
        serialized_data_ptr += SHA256_DIGEST_LENGTH;
    }
}

파일 리스트를 직렬화한 코드이다.

파일 리스트를 순회하면서 경로의 길이,경로,체크썸의 값을 직렬화하여 저장한다.

 

void file_list_deserialized(unsigned char **serialized_data, file_list_t *file_list, int file_count, char *sync_file_path)
{
    if (NULL == serialized_data || NULL == file_list)
    {
        printf("file_list_deserialized 매개변수가 올바르지 않습니다.\n");
        return;
    }
    // 직렬화 데이터의 포인터 설정
    unsigned char *serialized_data_ptr = NULL;
    serialized_data_ptr = *serialized_data;

    file_list_t *current_file_data;

    SHA256_CTX sha256;
    SHA256_Init(&sha256);

    FILE *file = NULL;
    file = fopen(sync_file_path, "w");
    // 직렬화 데이터 추출
    for (int path_index = 0; path_index < file_count; path_index++)
    {
        // 파일 경로 길이 추출 및 포인터 이동
        unsigned long file_path_size = 0;
        memcpy(&file_path_size, serialized_data_ptr, sizeof(file_path_size));
        serialized_data_ptr += sizeof(file_path_size);

        char path[MAX_LENGTH];
        memset(path, 0, sizeof(path));

        // 파일 경로 추출 및 포인터 이동
        snprintf(path, file_path_size, "%s", serialized_data_ptr);
        serialized_data_ptr += file_path_size;

        fprintf(file, "%s\n", path);

        unsigned char check_sum[SHA256_DIGEST_LENGTH];
        memset(check_sum, 0, sizeof(check_sum));

        // 파일 체크썸 추출 및 포인터 이동
        memcpy(check_sum, serialized_data_ptr, SHA256_DIGEST_LENGTH);
        serialized_data_ptr += SHA256_DIGEST_LENGTH;

        current_file_data = NULL;
        current_file_data = find_file_data(file_list, path);
        if (NULL == current_file_data)
        {
            add_path(&file_list, path);
        }
        else if (0 != strncmp(check_sum, current_file_data->check_sum, SHA256_DIGEST_LENGTH))
        {
            check_sum_generater(path, current_file_data->check_sum, &sha256);
            current_file_data->state = 1;
        }
    }
    fclose(file);
}

 파일 리스트를 역직렬화한 코드이다.

먼저 경로와 체크썸의 데이터를 추출한 후, 슬레이브 서버에 파일 리스트에 해당 경로가 존재하는 지 확인한다.

없다면 파일 리스트에 추가하고, 있다면 체크썸 값을 비교한다.

체크썸까지 일치하면 다음 경로로 넘어가고, 불일치하면 해당 파일 리스트의 state값을 1로 변경해 다음 업데이트 경로 직렬화에 사용한다.

 

업데이트 경로 직렬화, 역직렬


void file_path_serialized(unsigned char **serialized_data, transfer_header_t *transfer_header, file_list_t *file_list)
{
    if (NULL == file_list)
    {
        printf("file_list_serialized 매개변수가 올바르지 않습니다.\n");
        return;
    }

    // 직렬화 데이터 동적 할당
    *serialized_data = (unsigned char *)malloc(sizeof(transfer_header_t) + transfer_header->total_size);

    // 직렬화 데이터의 포인터 설정
    unsigned char *serialized_data_ptr = *serialized_data;

    // 헤더 저장 및 포인터 이동
    memcpy(serialized_data_ptr, transfer_header, sizeof(transfer_header_t));
    serialized_data_ptr += sizeof(transfer_header_t);

    file_list_t *current_file_data = NULL;
    file_list_t *tmp = NULL;

    HASH_ITER(hh, file_list, current_file_data, tmp)
    {
        if (1 == current_file_data->state)
        {
            memcpy(serialized_data_ptr, &current_file_data->file_path_size, sizeof(unsigned long));
            serialized_data_ptr += sizeof(unsigned long);

            snprintf(serialized_data_ptr, current_file_data->file_path_size, "%s", current_file_data->path);
            serialized_data_ptr += current_file_data->file_path_size;
        }
    }
}

업데이트 경로 직렬화 코드이다.

여기서 파일 리스트를 순회하며, state가 1인 요소의 경로를 직렬화하여 저장한다.

 

void file_path_deserialized(file_list_t *file_list, unsigned char **serialized_data, int file_count)
{
    if (NULL == *serialized_data && 0 == file_count)
    {
        printf("file_path_deserialized 매개변수가 올바르지 않습니다.\n");
        return;
    }
    
    unsigned char *serialized_data_ptr = NULL;
    serialized_data_ptr = *serialized_data;

    file_list_t *current_file_data = NULL;

    for (int path_index = 0; path_index < file_count; path_index++)
    {
        unsigned long file_path_size = 0;
        memcpy(&file_path_size, serialized_data_ptr, sizeof(file_path_size));
        serialized_data_ptr += sizeof(unsigned long);

        char path[MAX_LENGTH];
        memset(path, 0, sizeof(path));

        snprintf(path, file_path_size, "%s", serialized_data_ptr);
        serialized_data_ptr += file_path_size;

        current_file_data = find_file_data(file_list, path);
        if (NULL == current_file_data)
        {
            continue;
        }
        else
        {
            printf("업데이트: %s\n", current_file_data->path);
            current_file_data->state = 1;
        }
    }
}

업데이트 경로 역직렬화 코드이다.

동일하게 경로를 추출하고, 추출한 경로를 파일 리스트에서 찾아 state를 1로 변경한다.

이후, 파일 직렬화에서 state가 1인 경로를 전송한다.

 

만약 파일 리스트의 경로가 없다면, 무시하고 다음 경로로 넘어간다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함