일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 백준온라인저지
- Typescript
- 개발자북클럽
- 정렬
- 이코테
- 프로그래머스
- 알고리즘
- 빅데이터분석
- 그리디
- 다이나믹프로그래밍
- ps
- BOJ
- 이진탐색
- TS
- DP
- 백준
- 구현
- 코딩테스트
- 노마드코더
- 코딩일기
- react-native
- 최단경로
- SQL
- 타입스크립트
- 앱개발
- 이것이코딩테스트다
- bfs
- dfs
- 코테
- c++
- Today
- Total
한량처럼 살고 싶다
C언어 해시테이블 구현 본문
해시테이블은 자료가 방대할 때 사용하기 좋다.
- 아이피주소를 해싱한다
- 해싱한 아이피주소를 해시테이블 크기로 나눈 나머지 값을 구한다
- 이 때 구한 나머지 값이 바로 이 아이피주소의 키값이다.
- 해시테이블의 해당 키 위치에 아이피주소 노드를 연결한다.
- 이를 반복하다보면 아이피주소는 매우 많기 때문에 중복되는 키가 생기는데, 이걸 충돌이라 부른다
- 충돌이 일어나면, 그 키에 새로운 노드를 만들어 연결하여 아이피주소를 저장한다.
- 즉 배열에 노드를 저장하는데, 그 노드들은 이중연결리스트의 노드라서 같은 키를 가진 다른 아이피를 저장할 수 있게 된다.
0. 해시테이블 구조
사용자가 입력한 크기만큼의 일차원배열을 만든다.
배열에 저장되는 값은 HashTableHead로, 해당 키에 저장된 아이피주소의 개수와 다음노드를 저장할 수 있는 멤버변수를 갖는다.
아이피주소는 해시+나머지 연산 이후 나오는 값의 인덱스를 키로 갖는다.
HashTableNode를 만들어 일차원배열에서 해당 인덱스(==키)에 저장한다.
1. 헤더파일
링크드리스트에 사용될 구조체도 함께 선언되어있다.
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <stdint.h>
#include <pthread.h>
#include <time.h>
#define FLOOD_IP_ADDR_LEN 16
typedef struct {
int32_t tablesize;
int32_t timelimit;
int32_t count;
} FloodConfig;
typedef struct _HashTableNode {
unsigned int srcip;
int count;
time_t detecttime;
struct _HashTableNode *prev;
struct _HashTableNode *next;
} HashTableNode;
typedef struct {
int nodecnt;
HashTableNode *next;
} HashTableHead;
typedef struct {
HashTableHead *node;
int tablesize;
int timelimit;
int count;
pthread_mutex_t mutex;
} HashTable;
void initHashTable(HashTable *hashtable, FloodConfig *flood_config);
int isEmptyHashTable(HashTable *hashtable, int key);
HashTableNode* makeTableNode(unsigned int srcip);
HashTableNode* findTargetLocation(HashTable *hashtable, int key, unsigned srcip);
int insertNode(HashTable *hashtable, int key, unsigned int srcip, int isEmpty);
int hashSourceTp(HashTable *hashtable, unsigned int srcip);
int hasSrcIp(unsigned int srcip);
int checkTable(HashTable *hashtable, unsigned int srcip);
#endif
2. 소스파일
1) checkTable()
이 함수가 hashtable.c 의 메인함수 격이다. detect thread에서는 icmp 프로토콜이 들어왔을 때 이 함수를 호출한다.
int checkTable(HashTable *hashtable, unsigned int srcip) {
pthread_mutex_lock(&(hashtable->mutex));
uint32_t key = hashSrcIp(srcip) % (hashtable->tablesize);
int isEmpty = isEmptyHashTable(hashtable, key);
int insertResult = 0;
insertResult = insertNode(hashtable, key, srcip, isEmpty);
pthread_mutex_unlock(&(hashtable->mutex));
return insertResult;
}
2) hashSrcIp(), isEmptyHashTable()
아이피 해싱하여 키를 얻는다.
그리고 그 키를 이용하여 해시테이블의 해당 위치가 비어있는지 판별한다.
uint32_t hashSrcIp (unsigned int srcip) {
return hash_func4((const char *)&srcip, sizeof(srcip));
}
int isEmptyHashTable(HashTable *hashtable, int key) {
return (hashtable->node[key].nodecnt) ? NOT_EMPTY : EMPTY;
}
3) insertNode()
해당 키가 비어있다면 이제 들어가는 아이피 노드가 첫 번째 노드이고, 아니라면 다른 노드 뒤에 연결해주면 된다.
int insertNode(HashTable *hashtable, int key, unsigned int srcip, int isEmpty){
if (isEmpty){
hashtable->node[key].nodecnt = 1; //첫 번째
hashtable->node[key].next = makeTableNode(srcip);
hashtable->node[key].next->prev = NULL; //앞 노드는 HEAD이기 때문에 양방향 연결 x
return SUCCESS;
}
struct timespec detect_ts, current_ts;
clock_gettime(CLOCK_REALTIME, ¤t_ts);
HashTableNode *target_node = findTargetLocation(hashtable, key, srcip);
if (!target_node) return FAIL;
if (target_node->srcip != srcip) { //이 아이피는 리스트에 존재하지 않는다 == 끝에 넣어준다
hashtable->node[key].nodecnt += 1;
target_node->next = makeTableNode(srcip);
target_node->next->prev = target_node; //앞 뒤 연결해줌
return SUCCESS;
}
//이 아이피는 리스트에 존재한다
detect_ts.tv_sec = target_node->detecttime;
detect_ts.tv_nsec = 0;
long sec_diff = current_ts.tv_sec - detect_ts.tv_sec;
if (sec_diff > hashtable->timelimit) { //시간을 체크해봤는데 제한시간이 지났다
time_t detect_time;
time(&detect_time);
target_node->count = 0;
target_node->detecttime = detect_time;
return SUCCESS;
}
if (sec_diff <= hashtable->timelimit) { //시간을 체크해봤는데 제한시간이 안 지났다
target_node->count += 1;
if (target_node->count >= hashtable->count) { //제한 개수가 넘었다면
hashtable->node[key].nodecnt -= 1;
//내 앞이 헤드이면
if (target_node->prev == NULL) {
hashtable->node[key].next = target_node->next;
if (target_node->next != NULL)
target_node->next->prev = NULL;
free(target_node);
return FLOOD;
}
//내 앞에 노드가 있다면
target_node->prev->next = target_node->next;
if(target_node->next != NULL)
target_node->next->prev = target_node->prev;
free(target_node);
return FLOOD;
}
return SUCCESS;
}
return FAIL;
}
4) insertNode()에 필요한 함수들
makeTableNode()는 새로운 노드를 만들어 반환하는 함수이다.
findTargetLocation()은 새로운 노드를 넣어야 할 자리의 포인터를 반환하는 함수이다.
HashTableNode* makeTableNode(unsigned int srcip) {
time_t detect_time;
time(&detect_time);
HashTableNode *node = (HashTableNode *)malloc(sizeof(HashTableNode));
if (node == NULL) {
printf("해시테이블 저장에 문제가 생겼습니다. 프로그램을 종료합니다.\n");
exit(0);
}
node->srcip = srcip;
node->count = 1;
node->detecttime = detect_time;
node->next = NULL;
return node;
}
HashTableNode* findTargetLocation(HashTable *hashtable, int key, unsigned srcip){
HashTableNode *node = hashtable->node[key].next;
while(node != NULL) {
if (node->srcip == srcip) return node; //그 아이피를 가진 노드를 반환해준다
if (node->next == NULL) return node; //다음 위치가 null이면 (끝까지 찾은거니까) 이 아이피는 없는 아이피이므로 반환한다.
}
node = NULL;
return node;
}
원본 소스 🔽
https://github.com/Hayeon-Lee/IDS/blob/main/hashtable.c
IDS/hashtable.c at main · Hayeon-Lee/IDS
Contribute to Hayeon-Lee/IDS development by creating an account on GitHub.
github.com
https://github.com/Hayeon-Lee/IDS/blob/main/hashtable.h
IDS/hashtable.h at main · Hayeon-Lee/IDS
Contribute to Hayeon-Lee/IDS development by creating an account on GitHub.
github.com
'C C++ > IDS 개발 - C' 카테고리의 다른 글
IDS 프로젝트 - 정수로 된 IP주소를 해싱 (0) | 2024.04.25 |
---|---|
IDS 프로젝트 - MAKEFILE 작성하기 (0) | 2024.04.22 |
IDS 프로젝트 - 개발 후 유용하게 사용한 함수와 구조체 정리 (0) | 2024.04.19 |