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 Endian04 03 02 01

내 PC는 x86이라 Little Endian이다. 그러니까 NBO로 저장된 파일을 그냥 fread()로 읽으면 바이트 순서가 뒤집혀서 엉뚱한 값이 나온다. 이 변환을 처리해주는 함수가 바로 ntohl()network to host long이다.


코드 구조

파일 구조는 단순하다.

add-nbo/
├── main.cpp   # 메인 로직
├── add.h      # add() 함수 선언 (향후 확장용)
└── Makefile

main.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 *.o

makefile

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 → Host32비트
ntohs()Network → Host16비트
htonl()Host → Network32비트
htons()Host → Network16비트

이번 과제에는 AI를 전혀 쓰지 않고, 구글링도 하지 않고 내 머리와 멘토님이 주신 문서만 가지고 코드를 짜보았는데 머리가 정말 터지는 줄 알았다.

그래도 평생 잊지는 않을 것 같다.