티스토리 뷰

보통 c언어에서는 데이터 셋을 구조체를 통해 구현한다.

다른 언어가 객체와 같이 key-value 쌍으로 유연하게 속성을 추가하고, 관리할 수 있다.

그러나 c는 구조페를 선언할때 사용할 맴버를 미리 정해야하고, 구조체 배열에서 CRUD을 수행하려면 배열을 전부 순회하거나 새로 만드는 형식으로 밖에 구현이 불가능하다.

때문에 이번 프로젝트에서는 해쉬 테이블을 사용하여 CRUD를 좀 더 간편하고, 빠르게 구현할 것이다.

 

해쉬 테이블 구성


해쉬 테이블에는 uthash라는 라이브러리를 사용하며, UT_hash_handle라는 속성을 구조체에 추가하여 구현하였다.

struct file_list
{
    char *path;
    char *file_name;
    unsigned long file_path_size;
    unsigned long file_data_size;
    unsigned char check_sum[SHA256_DIGEST_LENGTH];
    long update_time;
    int state;
    UT_hash_handle hh;
};
typedef struct file_list file_list_t;

 

다음은 동기화 리스트를 읽고, 데이터를 저장할 구조체의 정의이다.

각 맴버의 속성을 설명하자면,

 

  • char *path: 파일이 저장된 경로. 파일을 탐색할 대 사용한다.
  • char *file_name : 파일명. 전송시 보내진다.
  • unsigned long file_path_size : 파일명의 길이. 
  • unsigned long file_data_size : 파일 데이터 크기
  • unsigned char check_sum[SHA256_DIGEST_LENGTH] : 파일 데이터의 체크썸. 다른 서버와의 통신에서 동기화 대상이 있는 지를 확인하기 위해 비교할 때 사용.
  • long update_time : 파일의 업데이트 시간
  • int state : 구조체의 상태. 0은 일반, 1은 변경, -1은 삭제를 의미한다.
  • UT_hash_handle hh : uthash 함수와 매크로를 사용하기 위한 핸들러

 

해쉬 테이블 추가


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

    file_list_t *current_file_data = NULL;

    HASH_FIND_STR(*file_list, path, current_file_data);
    char *file_name = NULL;
    if (NULL != current_file_data)
    {
        printf("%s는 이미 존재하는 경로입니다.\n", path);
        return;
    }

    struct stat file_data;
    memset(&file_data, 0, sizeof(file_data));
    stat(path, &file_data);

    SHA256_CTX sha256;
    SHA256_Init(&sha256);

    file_name = strrchr(path, '/');
    file_name++;

    current_file_data = (file_list_t *)malloc(sizeof(file_list_t));
    current_file_data->path = strndup(path, MAX_LENGTH);
    current_file_data->file_name = strndup(file_name, MAX_LENGTH);
    current_file_data->file_path_size = strlen(file_name) + 1;
    current_file_data->update_time = file_data.st_mtime;
    current_file_data->file_data_size = file_data.st_size;

    current_file_data->state = 1;
    check_sum_generater(path, current_file_data->check_sum, &sha256);

    HASH_ADD_KEYPTR(hh, *file_list, current_file_data->path, strlen(path), current_file_data);
    printf("%s가 추가되었습니다.\n", path);
}

 

해쉬 테이블에 요소를 추가하는 함수이다. 우선 path에 있는 경로를 키로 사용하는 데, 이 키를 통해 특정 경로의 구조체를 탐색할 수 있다.

 

우선 path를 키로 사용하는 요소, 즉 이미 추가된 경로인지 확인하고 없다면 계속 진행한다.

우선 file_list 구조체를 동적할당하고, 경로와 파일명을 할당한다.

stat으로 파일의 데이터를 가져와 다른 file_list 맴버의 값에 할당한다.

 

state의 경우, 이후 해쉬 테이블에서 변경된 파일만 전송하거나 특정 요소들만 제거할때 사용한다.

add_path 함수가 사용되는 경우, 클라이언트에 전송을 하기에 초기값은 1로 만든다.

이 후,전송이 끝나면 0으로 전부 초기화 시킨다.

 

체크썸의 경우, 다른 서버의 file_list와 비교하기 위해서 만든 맴버로 sha256으로 해싱하여 저장한다.

 

전부 끝난 후,HASH_ADD_KEYPTR 매크로로 경로를 키로 등록해 해쉬 테이블에 추가한다.

해쉬 테이블 탐


file_list_t *find_file_data(file_list_t *file_list, char *path)
{
    if (NULL == path)
    {
        printf("find_file_data의 매개변수가 올바르지 않습니다.\n");
        return NULL;
    }

    file_list_t *current_file_data = NULL;
    HASH_FIND_STR(file_list, path, current_file_data);

    if (NULL == current_file_data)
    {
        printf("%s를 찾을 수 없습니다.\n", path);
    }

    return current_file_data;
}

 

HASH_FIND_STR 매크로로 경로ㄹ를 통해 특정 file_list 데이터를 탐색하여 리턴한다. 

주로 변경사항을 확인하거나, 데이터를 삭제할 때 사용된다.

 

주의할 점은 HASH_ADD_KEYPTR으로 등록된 키의 값이외에 키의 크기도 같이 저장하는데,

값이 같아도 크기가 다르다면 기존의 요소라고 인식하지 않는다.

 

때문에 문자열을 키로 삼을 때, 주의해야한다.

해쉬 테이블 삭제


void delete_file_data(file_list_t **file_list, char *path)
{
    if (NULL == path)
    {
        printf("delete_file_data의 매개변수가 올바르지 않습니다.\n");
        return;
    }
    if (2 == relative_path_check(path))
    {
        path = absolute_path_change(path);
    }
    file_list_t *current_file_data = find_file_data(*file_list, path);

    if (NULL != current_file_data)
    {
        printf("%s가 삭제되었습니다.\n", path);
        free(current_file_data->path);
        free(current_file_data->file_name);
        free(current_file_data);
        HASH_DEL(*file_list, current_file_data);

        return;
    }

    printf("%s를 삭제하지 못했습니다.\n", path);
}

단일 테이블 삭제 함수로, 인자로 받은 경로로 탐색하여 해당 요소만 제거하는 함수이다.

이후 경로와 파일명,그리고 구조체는 동적할당을 하였기에 free로 해제한다.

 

이 함수를 사용해, 아래와 같이 -state가 -1인 요소만 삭제하거나, 전체 테이블을 삭제하는 함수에 이용한다.

void delete_state_clear(file_list_t **file_list)
{
    if (NULL == *file_list)
    {
        printf("delete_state_clear의 매개변수가 올바르지 않습니다.\n");
        return;
    }
    file_list_t *current_file_data = NULL;
    file_list_t *tmp = NULL;

    unsigned long total_size = 0;
    int file_count = 0;
    HASH_ITER(hh, *file_list, current_file_data, tmp)
    {
        if (-1 == current_file_data->state)
        {
            printf("제거 경로: %s\n", current_file_data->path);
            delete_file_data(file_list, current_file_data->path);
        }
    }
}

void clear_file_list(file_list_t **file_list)
{

    file_list_t *current_file_data, *tmp;
    HASH_ITER(hh, *file_list, current_file_data, tmp)
    {
        delete_file_data(file_list, current_file_data->path);
    }
    printf("file_list가 삭제되었습니다.\n");
}

 

HASH_ITER은 테이블 전체를 순회하는 일종의 for문이다.

 

해쉬 테이블 변경


 

void change_state(file_list_t *file_list, int state)
{
    file_list_t *current_file_data = NULL;
    file_list_t *tmp = NULL;

    HASH_ITER(hh, file_list, current_file_data, tmp)
    {
        current_file_data->state = state;
    }
}

각 테이블의 상태를 일괄적으로 변경시키는 함수이다.

 

프로젝트에서는 변경사항 확인시, 모든 테이블의 상태를 -1로 변경하였다.

 

이후 아래에 함수에서, 변경되지 않은 요소는 0으로, 변경된 요소는 1로 변경하였다.

int check_path(file_list_t *current_file_data, char *path)
{
    struct stat file_data;
    memset(&file_data, 0, sizeof(file_data));
    stat(path, &file_data);
    printf("path: %s", current_file_data->path);

    SHA256_CTX sha256;
    SHA256_Init(&sha256);

    if (current_file_data->update_time == file_data.st_mtime)
    {
        printf("유지\n");
        current_file_data->state = 0;
    }
    else
    {
        printf("변경\n");
        current_file_data->update_time = file_data.st_mtime;
        current_file_data->file_data_size = file_data.st_size;

        check_sum_generater(path, current_file_data->check_sum, &sha256);
        current_file_data->state = 1;
    }

    return current_file_data->state;
}

 

이후에, 상태가 -1인 요소는 기존 동기화 리스트에서 제거된 요소로 취급한다.

위에 있는 delete_state_clear함수로 -1인 요소를 테이블에서 제거한다.

 

이후 상태가 1인 요소들만 클라이언트에 전송하고, 모든 상태를 다시 0으로 변경한다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/05   »
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
글 보관함