UUID(Universally Unique Identifier)는 유일한 식별자를 생성하기 위해서 표준화된 방식으로 128비트 크기를 갖고 5개 구룹으로 나뉘어 32자리 16진수 문자열로 표현이 된다.
RFC 4122에 의하면 UUID는 버전이 여러 가지가 있는데 이번에 포스팅해 볼 내용은 v4 랜덤 기반 UUID이다.
1. UUID V4 형식: xxxxxxxx-xxxx-4xxx-10xx-xxxxxxxxxxxx
e.g) 93e41c6e-2091-41b9-b36b-c7ce94edc677
포맷은 이런 식으로 구성이 되고 네트워크 전문에 주로 사용이 된다. 웹소켓 전문을 송수신할 때 메시지 아이디가 겹치지 않아 유용하게 사용했던 것으로 기억한다. 구조를 보면 8 - 4 - 4 - 4 - 12로 되어있고 아래 표를 참조하면 될 것 같다.
2. UUID 필드 구성(128 비트 구조)
필드명 | 비트 크기 | 바이트 크기 | 설명 |
time_low | 32비트 | 4바이트 | 무작위 값 |
time_mid | 16비트 | 2바이트 | 무작위 값 |
time_hi_and_version | 16비트 | 2바이트 | 무작위 값 + 버전 정보(4비트) |
clock_seq_hi_and_reserved | 8비트 | 1바이트 | 무작위 값 + 10xx로 시작하는 변형 정보 |
clock_seq_low | 8비트 | 1바이트 | 무작위 값 |
node | 48비트 | 6바이트 | MAC 주소 또는 무작위 생성된 값 |
분석에 앞서 93e41c6e-2091-41b9-b36b-c7ce94edc677를 보면 clock_seq_hi_and_reserved가 헷갈릴 수가 있는데 b3는 16진수로 2진수로 표현을 하게 되면 10110011이 된다. 즉, 10xx형식으로 시작을 하는 것이다.
d063c2ef-1c5d-4ce9-95f2-1f55ff0a8f80 다른 난수의 clock_seq_hi_and_reserved를 봐도 10010101로 동일한 형식으로 시작하는 것을 볼 수 있다. 10xx로 표현되기 위해서 clock_seq_hi_and_reserved는 앞자리가 8 or 9 or a or b 중 하나로 채워져야 한다.
이제 필드별로 구분을 하기 위해서 uuid를 배열을 선언하게 되면 null 포함 배열의 크기는 37이어야 한다.
1) time_low ( uuid[0] ~ uuid[7], xxxxxxxx )는 4바이트 16진수로 rand()를 사용하여 rand() % 16를 하면 랜덤 한 16진수의 무작위 인덱스가 나오고 const char *hex = "0123456789abcdef"로 되어있는 배열에 랜덤 인덱스를 넣으면 4바이트의 무작위 한 16진수의 값이 나오게 된다. 즉, hex[ rand() % 16 ]에서 해당 값이 나오게 되는 것이다.
2) time_mid (uuid[9] ~uuid[12], xxxx )는 time_low와 동일하게 계산이 되며 2바이트의 16진수 값을 갖게 된다.
3) time_hi_and_version (uuid[14]~uuid[17], 4xxx )는 uuid[14]의 경우 버전을 나타내기 위해서 '4'로 고정이고 나머지는 time_low와 같은 랜덤 값이다.
4) clock_seq_hi_and_reserved (uuid[19]~uuid[22] , yxxx)는 아까 규칙에 의하면 10xx로 8, 9, a, b 중 하나로 채워진다.
5) clock_seq_low (uuid[24]~uuid[25], xx)는 time_low와 동일한 랜덤 값이다.
6) node (uuid[26]~uuid[35], xxxxxxxxxx)는 time_low와 동일한 랜덤 값이다.
uuid[8], uuid[13], uuid[18], uuid[23]는 하이픈('-') 구분 문자가 삽입된다.
3. UUID 생성 코드
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
char* generate_uuid(void);
int main(void)
{
for (int i = 0; i < 60; i++) {
printf("uuid: %s\r\n", generate_uuid());
Sleep(5);
}
return 0;
}
char * generate_uuid(void)
{
static char uuid[37];
const char* hex = "0123456789abcdef";
if (uuid == NULL) {
printf("memory allocate fail\r\n");
return NULL;
}
memset(uuid, 0x00, sizeof(uuid));
clock_t seed = clock(); // ms 단위 시드 생성
srand((unsigned int)seed); // 시드 초기화
for (size_t i = 0; i < 36; i++) {
switch (i) {
case 8:
case 13:
case 18:
case 23:
uuid[i] = '-';
break;
case 14:
uuid[i] = '4';
break;
case 19:
uuid[i] = hex[(rand() % 4) + 8]; // 8 or 9 or a or b
break;
default:
uuid[i] = hex[rand() % 16]; // 16 진수
break;
}
}
uuid[36] = '\0';
return uuid;
}
위에 uuid 필드 구성을 배열로 설명을 했기 때문에 코드 설명은 따로 필요 없을 것 같다. 딜레이를 적절히 주지 않으면 시간 변동이 없기 때문에 겹치게 될 가능성이 존재한다. 위에 코드처럼 5ms 기준으로 uuid를 60개를 생성 시 겹치는 uuid는 하나도 발견이 되지 않았다.
V4 기준으로 2^122 정도 되니 uuid가 겹칠 확률은 거의 0에 가깝다고 보면 될 것 같다.
'프로그래밍' 카테고리의 다른 글
WPF RichTextBox 행간격 조절 RichTextBox Control (1) | 2024.02.27 |
---|---|
cdecl과 stdcall의 차이점 __cdecl과 __stdcall 비교 (1) | 2024.01.09 |
[엑셀 코딩] MIDB 사용 방법 및 파서 만들기 (0) | 2022.11.13 |
[VSCode] 로그 파일 설정 및 Log File Highlighter 사용법 (0) | 2021.09.26 |
tortoise svn lock 해제 update 매크로 (0) | 2020.01.20 |