IP 문자열을 숫자로 변환하는 방법 (inet_pton 활용)

2025. 5. 8. 10:25Development👩🏻‍🦳

Basic Practice: Converting IP Address Strings using inet_pton() and inet_ntop()

소켓 주소 구조체(Socket Address Structure)

항목 설명

정의 네트워크 통신에 필요한 주소 정보를 담는 구조체
기본 구조체 struct sockaddr
필드 구성 sa_family: 주소 체계 (예: AF_INET),sa_data[14]: 실제 주소 데이터 저장 공간
역할 bind()connect()accept() 등 다양한 소켓 함수의 인자로 사용
플랫폼 차이 윈도우: **SOCKADDR**로 typedef- 리눅스sockaddr 그대로 사용
주소 체계에 따른 확장 실제 사용은 sockaddr_in (IPv4), sockaddr_in6 (IPv6) 등 구체적 구조체로 확장하여 사용
포인트 **sockaddr**은 다형성(Polymorphic) 목적의 기본 구조체이며, 실 사용 시는 캐스팅하여 적용

 


쉽게 말해:

**struct sockaddr**는 모든 주소 구조체‘공통 인터페이스’ 역할을 하며, 실제 사용은 sockaddr_in 등 구체 구조체확장되어 사용됩니다.

윈도우/리눅스 모두 이 구조체를 기반으로 통신 상대의 정보를 담아 소켓 함수에 전달합니다.


sockaddr 구조체 핵심 요약

필드명 설명

sa_family 주소 체계를 나타내는 16비트 정수값.예: AF_INET(IPv4), AF_INET6(IPv6) 등
sa_data[14] 실제 주소 정보(IP 주소, 포트 번호 등)를 저장하는 바이트 배열.주소 체계에 따라 해석 방식이 달라짐

사용 목적 및 확장 구조체

  • **sockaddr**은 대표 타입으로, 다양한 네트워크 프로토콜에서 공통적으로 사용하는 인터페이스입니다.
  • 실제 사용 시에는 해당 프로토콜에 맞는 구체 구조체를 사용하고, 함수 인자에는 sockaddr* 형식으로 형변환(casting) 하여 전달합니다.

프로토콜 사용 구조체

IPv4 (TCP/UDP) sockaddr_in
IPv6 (TCP/UDP) sockaddr_in6
블루투스 sockaddr_bth
로컬 통신 sockaddr_un (유닉스 도메인 소켓)

 


쉽게 말하면:

  • **sockaddr**은 ‘공통 인터페이스’ 역할만 합니다.
  • 실제로는 sockaddr_insockaddr_in6 등 전용 구조체를 사용하고,
  • 소켓 함수에 넘길 때만 **sockaddr***로 형변환합니다.

IPv4, IPv6, Bluetooth 프로토콜에서 사용하는 소켓 주소 구조체를 쉽게 이해할 수 있도록 핵심 정리한 내용입니다:


1. sockaddr_in (IPv4 전용 소켓 주소 구조체)

struct sockaddr_in {
    short           sin_family;     // 주소 체계 (AF_INET)
    unsigned short  sin_port;       // 포트 번호 (Network byte order)
    struct in_addr  sin_addr;       // IPv4 주소
    char            sin_zero[8];    // 구조체 크기 일치를 위한 채움 (사용 안 함)
};
  • 용도: IPv4 기반 TCP/UDP 통신용
  • 예시 설정: AF_INET, 포트는 htons(9000), IP는 inet_addr("127.0.0.1")

2. sockaddr_in6 (IPv6 전용 소켓 주소 구조체)

struct sockaddr_in6 {
    short           sin6_family;       // 주소 체계 (AF_INET6)
    u_short         sin6_port;         // 포트 번호
    u_long          sin6_flowinfo;     // 플로우 정보 (일반적으로 0)
    struct in6_addr sin6_addr;         // IPv6 주소
    u_long          sin6_scope_id;     // 스코프 ID (링크로컬 주소 등에서 사용)
};
  • 용도: IPv6 기반 TCP/UDP 통신용
  • 특징: 플로우 정보와 스코프 ID가 추가되어 좀 더 세밀한 제어 가능

3. sockaddr_bth (Bluetooth 소켓 주소 구조체 - Windows 기준)

struct sockaddr_bth {
    USHORT      addressFamily;  // 주소 체계 (AF_BTH)
    BTH_ADDR    btAddr;         // Bluetooth 디바이스 주소
    GUID        serviceClassId; // 서비스 클래스 UUID
    ULONG       port;           // 포트 (RFCOMM 채널 번호)
};
  • 용도: 블루투스 통신 (Windows에서만 사용 가능)
  • 특징: UUID 기반의 서비스 접근, Bluetooth 장치 주소 사용

비교 요약 표

구조체 이름 주소 체계 주요 필드 사용 용도

sockaddr_in AF_INET sin_portsin_addr IPv4 소켓 주소
sockaddr_in6 AF_INET6 sin6_portsin6_addr 등 IPv6 소켓 주소
sockaddr_bth AF_BTH btAddrserviceClassId Bluetooth 통신

[자주 사용하는 소켓 구조체 필드 기능 요약]

필드명 소속 구조체 기능 설명

sin_family sockaddr_in (IPv4) 주소 체계 지정 (AF_INET)
sin6_family sockaddr_in6 (IPv6) 주소 체계 지정 (AF_INET6)
sin_port sockaddr_in 포트 번호 (네트워크 바이트 순서로 htons() 사용)
sin6_port sockaddr_in6 포트 번호 (네트워크 바이트 순서로 htons() 사용)
sin_addr sockaddr_in IPv4 주소 (struct in_addr)
sin6_addr sockaddr_in6 IPv6 주소 (struct in6_addr)
sin_zero[8] sockaddr_in 구조체 크기 맞추기용 padding (실제 사용하지 않음)
sin6_flowinfo sockaddr_in6 패킷 흐름 식별 정보 (대부분의 상황에서 0)
sin6_scope_id sockaddr_in6 링크-로컬 IPv6 주소 등에서 인터페이스 식별자 지정 (예: 네트워크 인터페이스 번호)
```cpp
typedef struct sockaddr_in6 {
  ADDRESS_FAMILY sin6_family;
  USHORT         sin6_port;
  ULONG          sin6_flowinfo;
  IN6_ADDR       sin6_addr;
  union {
    ULONG    sin6_scope_id;
    SCOPE_ID sin6_scope_struct;
  };
} SOCKADDR_IN6_LH, *PSOCKADDR_IN6_LH, *LPSOCKADDR_IN6_LH;
```

**`typedef struct sockaddr_in6`** 정의는 IPv6 네트워크 소켓 주소 구조체를 정의한 것입니다. 나노 단위로 모든 요소를 **주석**, **해석**, **용도**, **자료형** 단위로 상세히 쪼개서 주석을 달고 설명드리겠습니다.

---

### **📘 전체 구조체 선언부**

```c
typedef struct sockaddr_in6 {
```

- **`typedef struct`** : 사용자 정의 자료형을 선언하면서 동시에 별칭을 정의
- **`sockaddr_in6`** : 구조체 이름 (IPv6 주소를 담는 전용 구조체)

---

### **🧩 멤버 1: `ADDRESS_FAMILY sin6_family;`**

```c
  ADDRESS_FAMILY sin6_family;
```

- 🔍 **정의**: 주소 체계 (Address family)
- 🔢 **자료형**: **`ADDRESS_FAMILY`** (보통 **`AF_INET6`**이 들어감, 값은 23)
- 📦 **용도**: 이 구조체가 IPv6용임을 시스템에게 알려주는 역할
- 📘 **예시값**: **`AF_INET6`** (23)

---

### **🧩 멤버 2: `USHORT sin6_port;`**

```c
  USHORT sin6_port;
```

- 🔍 **정의**: 포트 번호 (네트워크 바이트 오더)
- 🔢 **자료형**: **`USHORT`** (2바이트, 부호 없는 정수)
- 📦 **용도**: TCP/UDP 통신에서 사용하는 포트
- 💡 **주의**: **`htons()`** 함수로 바이트 오더를 변환해 사용해야 함

---

### **🧩 멤버 3: `ULONG sin6_flowinfo;`**

```c
  ULONG sin6_flowinfo;
```

- 🔍 **정의**: 흐름 정보 (IPv6 전용)
- 🔢 **자료형**: **`ULONG`** (4바이트 부호 없는 정수)
- 📦 **용도**: 실시간/스트리밍 트래픽 흐름을 식별하는 데 사용 (잘 안 쓰임)

---

### **🧩 멤버 4: `IN6_ADDR sin6_addr;`**

```c
  IN6_ADDR sin6_addr;
```

- 🔍 **정의**: 실제 IPv6 주소
- 🔢 **자료형**: **`IN6_ADDR`** 구조체
- 📦 **용도**: 128비트 IPv6 주소 값을 저장
- 🧠 **비고**: 이 필드에 **`inet_pton()`** 등으로 주소를 변환해서 넣음

---

### **🧩 멤버 5: 익명 union (범위 ID 또는 구조체 형태)**

```c
  union {
    ULONG    sin6_scope_id;
    SCOPE_ID sin6_scope_struct;
  };
```

- 🔍 **정의**: 범위 ID (Scope ID, 링크 로컬 주소의 인터페이스 식별자)
- 🔢 **자료형**:
    - **`ULONG sin6_scope_id`**: 숫자형 범위 ID
    - **`SCOPE_ID sin6_scope_struct`**: 구조체 형태 (Windows 환경 전용)
- 📦 **용도**: 같은 링크 내에 여러 인터페이스가 있는 경우, 특정 인터페이스 지정
- 💡 **비고**: **`fe80::/10`** 형태의 링크-**로컬 IPv6 주소**에서 중요함

---

### **🧩 전체 구조체 별칭 (타입 정의)**

```c
} SOCKADDR_IN6_LH, *PSOCKADDR_IN6_LH, *LPSOCKADDR_IN6_LH;
```

- **`SOCKADDR_IN6_LH`**: 구조체 이름
- **`PSOCKADDR_IN6_LH`**: 포인터 타입 (Pointer to SOCKADDR_IN6_LH)
- **`LPSOCKADDR_IN6_LH`**: 긴 이름의 포인터 (Long Pointer to SOCKADDR_IN6_LH)
- 📦 **용도**: 함수 인자로 구조체 포인터 전달 시 사용

---

### **✅ 전체 정리 요약 (표)**

| **필드 이름** | **자료형** | **의미 / 용도** |
| --- | --- | --- |
| **`sin6_family`** | ADDRESS_FAMILY | 주소 패밀리 (IPv6는 AF_INET6 = 23) |
| **`sin6_port`** | USHORT | 포트 번호 (16비트, 네트워크 바이트 오더) |
| **`sin6_flowinfo`** | ULONG | 흐름 정보 (QoS 관련 필드, 드물게 사용) |
| **`sin6_addr`** | IN6_ADDR | IPv6 주소 (128비트) |
| **`sin6_scope_id`** | ULONG | 링크 로컬 주소용 인터페이스 식별자 |
| **`sin6_scope_struct`** | SCOPE_ID | 위와 같은 목적의 구조체 (Windows 전용) |

이 필드들은 bind()connect()accept() 등 다양한 소켓 함수의 인수로 쓰이며, 네트워크 통신에서 주소를 정확히 지정하는 데 핵심적인 역할을 합니다.


**IP 주소를 저장하는 구조체인 in_addr (IPv4용)와 in6_addr (IPv6용)**의 핵심 구조와 기능을 간결하게 정리한 내용입니다:


[IP 주소 저장용 구조체 요약]

구조체명 사용 용도 필드명 자료형 설명

in_addr IPv4 주소 저장 s_addr uint32_t 32비트 IPv4 주소를 저장 (네트워크 바이트 순서)
in6_addr IPv6 주소 저장 s6_addr[16] unsigned char[16] 128비트 IPv6 주소를 저장
(윈도우 전용) (IPv4 내부 구조) S_un.S_addr u_long in_addr 내 union 필드, 주소 접근의 별칭
(윈도우 전용) (IPv6 내부 구조) Byte[16] UCHAR[16] in6_addr 내 union 필드, 바이트 단위 접근
(윈도우 전용) (IPv6 내부 구조) Word[8] USHORT[8] 16비트 단위로 IPv6 주소 접근

예시 요약 정리

  • IPv4 주소 저장in_addr 구조체의 **s_addr**에 저장
    • 예: inet_addr("192.168.0.1") 결과값을 **s_addr**에 저장
  • IPv6 주소 저장in6_addr 구조체의 **s6_addr[16]**에 바이트 배열 형태로 저장
    • 예: inet_pton(AF_INET6, "::1", &sockaddr_in6.sin6_addr);

NOTE:

  • 윈도우의 **struct in_addr**는 **union**으로 구성되어 있어 s_addrs_un_bs_un_w 등의 방식으로 세분 접근이 가능함.
  • 리눅스에서는 **in_addr**과 in6_addr 모두 단순한 데이터 배열로 정의되어 있음.

[소켓 주소 구조체 핵심 정리]

1. 구조체 역할

  • 네트워크 주소(IP/포트 등)를 담아 소켓 함수에 전달할 때 사용하는 구조체.
  • 사용하는 통신 프로토콜에 따라 다른 구조체를 사용함.

2. 대표 구조체와 파생 구조체

구조체 이름 용도 크기 (byte) 비고

sockaddr 공통 인터페이스 타입 16 모든 소켓 주소 구조체는 이 타입으로 캐스팅
sockaddr_in IPv4 주소 16 AF_INETin_addr 사용
sockaddr_in6 IPv6 주소 28 AF_INET6in6_addr 사용
sockaddr_bth Bluetooth 주소 30 AF_BTH, 블루투스 전용

소켓 함수 호출 시 반드시 sockaddr* 타입으로 캐스팅해야 함:예: (struct sockaddr*)&addr


3. 주요 필드 설명

필드명 역할 사용 구조체

sin_family / sin6_family 주소 체계 (IPv4: AF_INET, IPv6: AF_INET6) sockaddr_insockaddr_in6
sin_port / sin6_port 포트 번호 (16비트, network byte order) sockaddr_insockaddr_in6
sin_addr / sin6_addr IP 주소 in_addr / in6_addr

4. 주소 구조체 전달 시 유의사항

  • 소켓 함수에 구조체를 직접 전달 X, 항상 포인터 전달 O
  • 전달 전 struct sockaddr* 타입으로 명시적 형 변환 필요
  • **sizeof()**를 이용해 정확한 구조체 크기 전달 필수

5. 실제 사용 예시

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");

bind(sock, (struct sockaddr*)&addr, sizeof(addr));

[소켓 주소 구조체 사용 핵심 요약]

1. 사용 패턴: 두 가지 방식

  1. 입력용으로 사용 (초기화 후 전달)
struct sockaddr_in addr;
// 필드값 초기화
SocketFunc(..., (struct sockaddr*)&addr, sizeof(addr), ...);
  1. 출력용으로 사용 (함수가 값을 채움)
struct sockaddr_in addr;
int addrlen = sizeof(addr);
SocketFunc(..., (struct sockaddr*)&addr, &addrlen, ...);

주의: 항상 포인터로 전달, 타입은 (struct sockaddr*)으로 형 변환 필수


2. 선언 방법 요약

선언 형태 설명

struct sockaddr_in addr; 가장 일반적인 C 언어 방식
SOCKADDR_IN addr; 윈도우 전용 typedef 방식
Sockaddr_in addr; 일부 컴파일러(C++)에서 허용되는 스타일

3. 전달 방식 요약

전달 형태 대상 환경/표준

(struct sockaddr*)&addr C, 리눅스, 윈도우 공통
(sockaddr*)&addr C++ 호환 스타일
(SOCKADDR*)&addr 윈도우 전용 스타일

4. 코딩 스타일 추천

  • 사용 권장 스타일:
  • c 복사편집 struct sockaddr_in addr; SocketFunc(..., (struct sockaddr*)&addr, sizeof(addr), ...);
  • 리눅스/윈도우 양쪽 호환을 위해 항상 struct sockaddr* 타입으로 캐스팅

[바이트 정렬 함수 (Byte Order Functions)] 에 대한 핵심 요약입니다:


[02. 바이트 정렬 (Byte Order) 핵심 정리]

1. 바이트 정렬이란?

  • Byte Order (바이트 순서): 메모리에 숫자를 저장할 때 바이트를 나열하는 순서
  • 두 가지 방식:
    • Big-endian: 최상위 바이트(MSB)를 가장 앞에 저장 (0x12345678 → 0x12 0x34 0x56 0x78)
    • Little-endian: 최하위 바이트(LSB)를 가장 앞에 저장 (0x12345678 → 0x78 0x56 0x34 0x12)
  • 그림 예시:주소 Big-endian Little-endian
    0x1000 0x12 0x78
    0x1001 0x34 0x56
    0x1002 0x56 0x34
    0x1003 0x78 0x12

2. 왜 중요할까?

  • 다른 CPU 아키텍처 간 데이터 통신 시,
  • 파일 저장/읽기 또는 네트워크 전송/수신 시,
  • 엔디언이 다르면 데이터 해석이 오류 발생.

3. 네트워크에서의 바이트 정렬 기준

  • 네트워크는 항상 Big-endian 사용 (Network Byte Order)
  • → 리틀 엔디언 기반 시스템에서 전송 시 변환 필수

4. 바이트 정렬 변환 함수 (C 언어 기준)

함수명 설명

htons() Host → Network (16비트 short)
htonl() Host → Network (32비트 long)
ntohs() Network → Host (16비트 short)
ntohl() Network → Host (32비트 long)
  • h: host
  • n: network
  • s: short (16비트)
  • l: long (32비트)

5. 예제

c
복사편집
uint16_t port = 9000;
uint16_t net_port = htons(port); // 포트를 네트워크 바이트 오더로 변환

uint32_t ip = 0xC0A80001; // 192.168.0.1
uint32_t net_ip = htonl(ip);      // IP를 네트워크 바이트 오더로 변환


[바이트 정렬이 중요한 3가지 상황]

① IP 주소 처리 문제 [그림 3-2 (a)]

  • 문제 요약: 호스트와 라우터가 서로 다른 바이트 정렬을 사용하면 IP 주소를 잘못 해석하여 잘못된 위치로 라우팅됨.
  • 해결 방식: 모든 시스템에서 IP 주소를 빅 엔디언(Network Byte Order) 으로 통일.

② 포트 번호 처리 문제 [그림 3-2 (b)]

  • 문제 요약: 호스트 간 바이트 정렬이 다르면 포트 번호를 다르게 해석해, 데이터가 잘못된 프로세스로 전달될 수 있음.
  • 해결 방식: 포트 번호도 항상 빅 엔디언(Network Byte Order) 으로 통일하여 전송.

③ 응용 프로그램 데이터 처리 문제 [그림 3-2 (c)]

  • 문제 요약: 응용 프로그램에서 전송하는 데이터(예: 구조체, 숫자 값 등)가 서버/클라이언트의 바이트 정렬 차이로 인해 잘못 해석될 수 있음.
  • 해결 방식: 바이트 정렬 변환 함수(htonlhtonsntohlntohs)를 사용하거나 직렬화/역직렬화(serialization/deserialization) 로 처리.

[핵심 용어 정리]

용어 의미

Host Byte Order 시스템이 내부적으로 사용하는 바이트 정렬 방식 (예: 리틀 엔디언)
Network Byte Order (NBO) 네트워크 전송 시 사용하는 표준 바이트 정렬 (항상 빅 엔디언)
htonl / htons Host → Network 변환 함수
ntohl / ntohs Network → Host 변환 함수

다음은 응용 프로그램 데이터에서 발생하는 바이트 정렬 문제에 대한 핵심 요약입니다:


[③ 응용 프로그램 데이터 문제 정리 – 그림 3-2(c)]

문제 상황 요약

  • 서로 다른 시스템(CPU 아키텍처)이 통신할 경우, 데이터의 바이트 정렬 방식(Endian) 이 다르면 숫자 값이 왜곡되거나 잘못 해석됨.
  • 예: 0x1234를 전송했는데 수신 측에서는 0x3412로 인식하는 현상 발생 가능.
  • 특히 구조체, 포트 번호, ID값, 길이 정보 등 숫자 기반 필드에서 문제가 큼.

[바이트 정렬 통일 전략]

상황 대응 방법

클라이언트-서버 모두 제작 두 시스템이 하나의 Endian 방식(보통 빅 엔디언)으로 맞춰 구현
서버가 이미 존재함 클라이언트는 서버의 바이트 정렬 방식을 따라야 함

[Endian 변환 함수 정리]

함수명 용도 설명

htons() Host to Network Short 16비트 값 → 빅 엔디언으로 변환
htonl() Host to Network Long 32비트 값 → 빅 엔디언으로 변환
ntohs() Network to Host Short 16비트 값 → 호스트 방식으로 변환
ntohl() Network to Host Long 32비트 값 → 호스트 방식으로 변환
  • 윈도우용<winsock2.h>
  • 리눅스용<arpa/inet.h>

[함수 호출 위치 요약]

데이터 전송 시

  • hton*() 함수 호출 후 → 소켓 함수에 전달
  • → 데이터를 네트워크 바이트 정렬(NBO) 로 변환
cpp
복사편집
uint16_t port = 9000;
uint16_t network_port = htons(port); // 변환 후 send()

데이터 수신 시

  • 소켓 함수 수신 후 → ntoh*()로 변환
  • → 수신한 데이터를 호스트 바이트 정렬(HBO) 로 복원
cpp
복사편집
uint32_t received_ip;
recv(sock, &received_ip, sizeof(received_ip), 0);
uint32_t host_ip = ntohl(received_ip);


다음은 sockaddr_in / sockaddr_in6 구조체의 바이트 정렬 개념 요약과 실습 의도 정리입니다:


1. 구조체 바이트 정렬 요약

[IPv4용] sockaddr_in 구조체

struct sockaddr_in {
    short sin_family;        // 주소 체계: AF_INET
    unsigned short sin_port; // 포트 번호 (Network Byte Order)
    struct in_addr sin_addr; // IP 주소 (Network Byte Order)
    char sin_zero[8];        // 미사용, 0으로 초기화
};

[IPv6용] sockaddr_in6 구조체

struct sockaddr_in6 {
    short sin6_family;        // 주소 체계: AF_INET6
    u_short sin6_port;        // 포트 번호 (Network Byte Order)
    u_long sin6_flowinfo;     // 흐름 제어 정보 (일반적으로 0)
    struct in6_addr sin6_addr;// IPv6 주소 (16바이트)
    u_long sin6_scope_id;     // 링크 범위 ID
};
  • 이 구조체들은 메모리 상에 순서대로 정렬되어 저장되며, **정렬 순서는 시스템 바이트 정렬 방식(호스트 바이트 순서)**를 따름.
  • 단, sin_portsin_addr 등의 필드는 반드시 **네트워크 바이트 정렬(Network Byte Order, NBO)**로 저장되어야 함.

2. 실습 3-1 요약 – 바이트 정렬 함수 사용 연습

  • ByteOrder.cpp 실습은 htons, htonl, ntohs, ntohl 함수의 실제 동작 확인을 위한 코드 작성 예제.
  • 목적:
    1. 호스트 바이트 정렬 → 네트워크 바이트 정렬 변환 (htons / htonl)
    2. 네트워크 바이트 정렬 → 호스트 바이트 정렬 복원 (ntohs / ntohl)
    3. 데이터 타입이 다른 함수에 잘못된 크기 입력 시 결과 확인

3. 구조 시각화: sockaddr_in의 바이트 순서 예시

주소(offset) 내용 설명

0–1 sin_family 주소 체계 (AF_INET = 2)
2–3 sin_port 포트 번호 (NBO, htons 사용)
4–7 sin_addr IP 주소 (NBO, htonl 사용)
8–15 sin_zero 항상 0으로 초기화

핵심 정리: 바이트 정렬 실습 요약]

항목 변수 설명

x1 0x1234 16비트 숫자 (호스트 바이트 순서)
y1 0x12345678 32비트 숫자 (호스트 바이트 순서)
htons(x1) 0x3412 16비트 → 네트워크 바이트 순서
htonl(y1) 0x78563412 32비트 → 네트워크 바이트 순서
ntohs(0x3412) 0x1234 네트워크 → 호스트 순서 복원
ntohl(0x78563412) 0x12345678 네트워크 → 호스트 순서 복원
htonl(x1) 0x34120000 잘못된 사용: 16비트를 32비트 함수에 전달하여 상위 2바이트가 0으로 채워짐

[출력 결과 해석]

[호스트 바이트-> 네트워크 바이트]
0x1234 → 0x3412
0x12345678 → 0x78563412

[네트워크 바이트-> 호스트 바이트]
0x3412 → 0x1234
0x78563412 → 0x12345678

[잘못된 사용 예]
0x1234 → 0x34120000
  • 변환 정확성 확인:
    • **htons(x1)**과 **ntohs(x2)**는 서로를 정확히 복원합니다.
    • **htonl(y1)**과 ntohl(y2) 역시 정상 작동합니다.
  • 오류 예제:
    • **htonl(x1)**의 결과는 하위 2바이트만 변환되고 상위 2바이트는 0이 되므로 잘못된 데이터 전송 원인이 됩니다.

추천 패턴

  • 포트번호IPv4 주소 → htons, htonl
  • 수신 포트수신 주소 → ntohs, ntohl
  • 자료형과 바이트 수 일치 확인: **u_short**엔 htons, **u_long**엔 htonl

좋습니다. 아래는 핵심 요약과 함께 실무 적용 포인트를 정리한 내용입니다.


핵심 요약: 03. IP 주소 변환 함수

항목 설명

IP 주소 입력 방식 사용자로부터 IP 주소를 입력받는 두 가지 주요 방식:① 명령행 인수 (예: ping 192.168.0.1)② GUI 위젯(Control) (예: FTP 접속창의 입력란)
문제 입력은 문자열(String) 형태이므로, 네트워크 통신을 위해선 숫자형(Binary)으로 변환해야 함
목표 문자열 IP 주소 → in_addr 또는 in6_addr 구조체로 변환 (32비트 IPv4 / 128비트 IPv6)

실무에서 주의할 점

  • 문자열 **"192.168.0.1"**은 실제 통신에는 사용할 수 없음.
  • socket()connect()bind() 등에서 사용할 구조체 필드 sin_addr 또는 **sin6_addr**는 숫자형이어야 함.
  • 따라서 IP 주소 변환 함수 사용은 반드시 선행되어야 함.

이후 학습 내용 (예고)

  • inet_pton() / inet_ntop()
  • → 문자열 ↔ 바이너리 IP 변환 함수
  • getaddrinfo() / getnameinfo()
  • → 도메인 이름과 포트까지 처리하는 고급 API

핵심 요약: IP 주소 변환 함수 (inet_pton / inet_ntop)

항목 설명

문제 IP 주소는 사용자에게 문자열(예: "192.168.0.1")로 입력받지만, 소켓 함수는 숫자형 (Binary) 주소를 필요로 함
해결책 다음 두 가지 함수로 변환 필요
inet_pton() Presentation → Numeric (문자열 → 숫자 IP)
inet_ntop() Numeric → Presentation (숫자 IP → 문자열)
지원 주소 체계 AF_INET (IPv4), AF_INET6 (IPv6)
용도 connect(), **bind()**에서 주소 초기화 시 사용 / 로그 출력 시 IP를 다시 문자열로 표시할 때 사용
사용 예 c<br>struct sockaddr_in addr;<br>inet_pton(AF_INET, "192.168.0.1", &addr.sin_addr);<br>c<br>char ipstr[INET_ADDRSTRLEN];<br>inet_ntop(AF_INET, &addr.sin_addr, ipstr, sizeof(ipstr));<br>

보충 정보

함수 의미 사용 목적

inet_pton() presentation → numeric 사용자 입력 IP를 binary로
inet_ntop() numeric → presentation 구조체에 저장된 IP를 문자열로 출력

NOTE: 전용 위젯의 한계

  • 그림 3-7에 나오는 IP 주소 전용 위젯은 사용자가 4개의 필드에 직접 숫자만 입력하도록 제한해 편리하지만, 다음 단점이 있음:
    • IPv6 미지원
    • 콘솔 기반 프로그램에는 부적합
    • 호환성 낮음 (GUI 전용)

핵심 요약: sockaddr_in 구조체에 바이트 정렬 및 IP 주소 변환 함수 적용하기

항목 내용

주소 초기화 **memset(&addr, 0, sizeof(addr));**를 통해 구조체 전체를 0으로 초기화
주소 체계 지정 addr.sin_family = AF_INET; (IPv4), AF_INET6 (IPv6)
IP 주소 변환 **inet_pton(AF_INET, "147.46.114.70", &addr.sin_addr);**→ 문자열 → 네트워크 바이트 정렬 32비트 주소
포트 번호 변환 **addr.sin_port = htons(9000);**→ 호스트 바이트 → 네트워크 바이트로 변환
소켓 함수에 전달 반드시 sockaddr* 타입으로 형변환: (struct sockaddr *)&addr
길이 전달 sizeof(addr) 또는 리눅스에서는 socklen_t addrlen = sizeof(addr);

반대 방향: IP 주소와 포트 출력

항목 내용

소켓 함수 결과 수신 SocketFunc(..., (struct sockaddr *)&addr, &addrlen, ...);
IP 주소 출력 char ipstr[INET_ADDRSTRLEN];inet_ntop(AF_INET, &addr.sin_addr, ipstr, sizeof(ipstr));
포트 번호 출력 **ntohs(addr.sin_port);**→ 네트워크 바이트 → 호스트 바이트 변환 필요
전체 출력 예시 printf("[TCP 서버] 클라이언트 접속: IP 주소=%s, 포트 번호=%d\\n", ipstr, ntohs(addr.sin_port));

보충 NOTE

  • inet_ntop()의 마지막 인자:
    • 윈도우: size_t
    • 리눅스: socklen_t (이 타입이 아니면 컴파일 오류 발생 가능)
  • 구형 함수inet_addr()inet_ntoa() → IPv4 전용, 비추천
  • 윈도우 전용 함수WSAStringToAddress()WSAAddressToString() → IPv4/IPv6 지원

예제 코드 요약

c
복사편집
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, "147.46.114.70", &addr.sin_addr);
addr.sin_port = htons(9000);
SocketFunc(..., (struct sockaddr*)&addr, sizeof(addr), ...);

c
복사편집
char ipstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr.sin_addr, ipstr, sizeof(ipstr));
printf("Client IP: %s, Port: %d\\n", ipstr, ntohs(addr.sin_port));


지금 보신 실습 예제는 IP 주소 변환 함수 사용법을 실제로 익히는 데 매우 효과적입니다. 핵심 내용을 다음과 같이 정리해드리겠습니다:


핵심 요약: IP 주소 변환 실습 정리

항목 설명

목적 문자열 형태의 IPv4, IPv6 주소를 → 숫자로 변환하고 → 다시 문자열로 복원하여 비교
사용 함수 inet_pton() : 문자열 → 숫자 (네트워크 바이트 정렬)inet_ntop() : 숫자 → 문자열
상수 정의 INET_ADDRSTRLEN : IPv4 문자열 최대 길이 (16)INET6_ADDRSTRLEN : IPv6 문자열 최대 길이 (46)
구조체 사용 in_addr (IPv4)in6_addr (IPv6)

예제 코드 흐름 요약

cpp
복사편집
// IPv4 변환 연습
const char *ipv4test = "147.46.114.70";
printf("IPv4 주소(변환 전) = %s\\n", ipv4test);

// 문자열 -> 네트워크 바이트 정렬 숫자
struct in_addr ipv4num;
inet_pton(AF_INET, ipv4test, &ipv4num);

// 숫자 -> 문자열로 복원
char ipv4str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ipv4num, ipv4str, sizeof(ipv4str));

printf("IPv4 주소(변환 후) = %s\\n", ipv4str);

cpp
복사편집
// IPv6 변환 연습
const char *ipv6test = "2001:db8:63b3:1::3490";
printf("IPv6 주소(변환 전) = %s\\n", ipv6test);

// 문자열 -> 숫자
struct in6_addr ipv6num;
inet_pton(AF_INET6, ipv6test, &ipv6num);

// 숫자 -> 문자열
char ipv6str[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ipv6num, ipv6str, sizeof(ipv6str));

printf("IPv6 주소(변환 후) = %s\\n", ipv6str);


실행 결과 예시

nginx
복사편집
IPv4 주소(변환 전) = 147.46.114.70
IPv4 주소(변환 후) = 147.46.114.70

IPv6 주소(변환 전) = 2001:db8:63b3:1::3490
IPv6 주소(변환 후) = 2001:db8:63b3:1::3490


TIP

  • 입력과 출력이 정확히 일치해야 변환이 제대로 된 것이다.
  • inet_pton() → 실패 시 0 또는 1 반환. 오류 체크도 중요.
  • inet_ntop() → 리턴값이 **NULL**이면 실패.

핵심 요약: IP 주소 변환 실습 (inet_pton / inet_ntop)

항목 설명

목적 문자열 형태의 IP 주소를 네트워크 바이트 정렬 숫자로 변환 후, 다시 문자열로 복원
사용 함수 inet_pton() : 문자열 → 네트워크 바이트 정렬 (binary)inet_ntop() : 네트워크 바이트 정렬 → 문자열
변환 대상 IPv4 : in_addr.s_addr (4바이트)IPv6 : in6_addr.s6_addr[16] (16바이트)
출력 포맷 %#x : 16진수 숫자 출력**%s** : 문자열 형태 IP 출력
주소 비교 기준 최종 문자열 출력이 원래 문자열과 동일해야 변환 성공

코드 핵심 흐름 (요약)

// IPv4
const char *ipv4test = "147.46.114.70";
inet_pton(AF_INET, ipv4test, &ipv4num);          // 문자열 → 숫자
inet_ntop(AF_INET, &ipv4num, ipv4str, sizeof(ipv4str)); // 숫자 → 문자열

// IPv6
const char *ipv6test = "2001:0230:abcd:ffab:0023:eb00:ffff:1111";
inet_pton(AF_INET6, ipv6test, &ipv6num);         // 문자열 → 숫자
inet_ntop(AF_INET6, &ipv6num, ipv6str, sizeof(ipv6str)); // 숫자 → 문자열

실행 결과 요약

IPv4 주소(변환 전)         = 147.46.114.70
IPv4 주소(숫자 변환 후)    = 0x46722e93 (네트워크 바이트 순서)
IPv4 주소(다시 문자열 변환)= 147.46.114.70

IPv6 주소(변환 전)         = 2001:0230:abcd:ffab:0023:eb00:ffff:1111
IPv6 주소(숫자 변환 후)    = 0x20010230abcdffab0023eb00ffff1111
IPv6 주소(다시 문자열 변환)= 2001:230:abcd:ffab:23:eb00:ffff:1111

※ IPv6 주소는 자동 압축(::) 및 0 생략 규칙에 따라 포맷이 바뀔 수 있으나, 의미는 동일합니다.


TIP 요약

  • inet_pton() 사용 시 실패하면 0 또는 1 반환 (오류 체크 필수)
  • **inet_ntop()**은 변환 실패 시 NULL 반환
  • IPv4 주소 길이 제한: INET_ADDRSTRLEN = 16IPv6INET6_ADDRSTRLEN = 46