https://github.com/ye11oc4t/add-nbo
NBO(Network Byte Order)로 저장된 값 읽어서 덧셈하기
네트워크 패킷을 다루다 보면 반드시 마주치는 개념이 있다. 바로 바이트 오더(Byte Order)다. 이번 실습에서는 NBO(Network Byte Order)로 저장된 바이너리 파일 두 개를 읽어서, 호스트 바이트 오더로 변환한 뒤 덧셈 결과를 출력하는 간단한 C++ 프로그램을 작성했다.
컴퓨터는 멀티바이트 정수를 메모리에 저장할 때 두 가지 방식 중 하나를 사용한다.
- Little Endian: 낮은 주소에 LSB(최하위 바이트) 먼저 저장. x86/x86-64가 여기에 해당.
- Big Endian: 낮은 주소에 MSB(최상위 바이트) 먼저 저장. 네트워크 표준(NBO)이 바로 이것.
예를 들어 0x01020304라는 32비트 값을 저장할 때:
| 방식 | 메모리 순서 (낮은 주소 → 높은 주소) |
|---|---|
| Big Endian (NBO) | 01 02 03 04 |
| Little Endian | 04 03 02 01 |
내 PC는 x86이라 Little Endian이다. 그러니까 NBO로 저장된 파일을 그냥 fread()로 읽으면 바이트 순서가 뒤집혀서 엉뚱한 값이 나온다. 이 변환을 처리해주는 함수가 바로 ntohl() — network to host long이다.
코드 구조
파일 구조는 단순하다.
add-nbo/
├── main.cpp # 메인 로직
├── add.h # add() 함수 선언 (향후 확장용)
└── Makefilemain.cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
FILE *fp = fopen(argv[1], "r");
FILE *fp1 = fopen(argv[2], "r");
uint32_t n1, n2;
fread(&n1, sizeof(n1), 1, fp);
fread(&n2, sizeof(n2), 1, fp1);
uint32_t num1 = ntohl(n1);
uint32_t num2 = ntohl(n2);
n1 = num1;
n2 = num2;
printf("%d(0x%x) + %d(0x%x) = %d(0x%x)\n",
n1, n1, n2, n2, n1+n2, n1+n2);
exit(0);
}add.h
all: add-nbo
add-nbo: main.o
g++ -o add-nbo main.o
main.o: add.h main.cpp
g++ -c -o main.o main.cpp
clean:
rm -f add-nbo
rm -f *.omakefile
all: add-nbo
add-nbo: main.o
g++ -o add-nbo main.o
main.o: add.h main.cpp
g++ -c -o main.o main.cpp
clean:
rm -f add-nbo
rm -f *.o핵심 흐름 분석
① 바이너리 파일에서 읽기
cpp
fread(&n1, sizeof(n1), 1, fp);fread()로 4바이트를 그대로 읽는다. 이 시점에서 n1에는 NBO 상태의 raw 바이트가 들어있다. 아직 변환 전이라 x86에서 그대로 출력하면 바이트 순서가 뒤집힌다.
② ntohl()로 변환
cpp
uint32_t num1 = ntohl(n1);
```
`ntohl()`이 Big Endian → Little Endian 변환을 처리해준다. `<arpa/inet.h>`에 선언되어 있고, 내부적으로는 바이트 스왑 연산이다. 만약 실행 환경이 Big Endian이라면 `ntohl()`은 no-op으로 동작한다 — 플랫폼 독립적인 코드가 되는 셈이다.
**③ 덧셈 및 출력**
변환된 값을 십진수와 16진수 동시에 출력한다.
```
1(0x1) + 2(0x2) = 3(0x3)빌드 및 실행
bash
make
./add-nbo file1.bin file2.bin테스트용 바이너리 파일은 Python으로 간단히 만들 수 있다.
python
import struct
with open("file1.bin", "wb") as f:
f.write(struct.pack(">I", 1)) # Big Endian으로 숫자 1 저장
with open("file2.bin", "wb") as f:
f.write(struct.pack(">I", 2))
```
실행 결과:
```
1(0x1) + 2(0x2) = 3(0x3)정리 및 느낀점
이번 실습의 핵심은 ntohl() 한 줄이다. 네트워크 프로그래밍에서는 패킷 헤더 파싱할 때마다 이 변환이 필요하다. libpcap으로 패킷 캡처를 하다 보면 IP 헤더, TCP 헤더의 포트/길이 필드 등을 읽을 때 자연스럽게 ntohs(), ntohl()을 쓰게 된다. 단순해 보이는 실습이지만 raw 바이너리 → 호스트 값으로 이어지는 흐름을 손으로 직접 확인하는 게 중요하다.
관련 함수 정리:
| 함수 | 방향 | 대상 |
|---|---|---|
ntohl() | Network → Host | 32비트 |
ntohs() | Network → Host | 16비트 |
htonl() | Host → Network | 32비트 |
htons() | Host → Network | 16비트 |
이번 과제에는 AI를 전혀 쓰지 않고, 구글링도 하지 않고 내 머리와 멘토님이 주신 문서만 가지고 코드를 짜보았는데 머리가 정말 터지는 줄 알았다.
그래도 평생 잊지는 않을 것 같다.