IP 주소 변환 함수

bolee·2022년 4월 5일
0

(3-7 그림 편집 컨트롤)

윈도우 응용 프로그램의 경우 위의 그림들과 같이 임의의 IP 주소를 입력받는데 명령행 인자를 사용하거나 편집 컨트롤(edit control)을 이용한다.
이때 응용 프로그램은 IP 주소를 문자열 형대로 전달받기 때문에, 네트워크 통신을 위해 이를 32비트(IPv4) 또는 128비트(IPv6) 숫자로 변환해야 한다.

(3-8 그림)

위 그림 처럼 윈도우의 IP 주소 컨트롤(IP address control)을 이용하면 IP 주소를 입력 받아 문자열 또는 32비트 숫자(네트워크 바이트 정렬)를 얻을 수 있다. 하지만 IPv6에는 사용할 수 없다는 단점이 있고, 콘솔 응용 프로그램에서는 사용할 수 없기 때문에 범용적이지 못하다.

IP 주소 변환 윈속 함수

inet_addr, inet_ntoa

응용 프로그램에서 IP 주소를 편리하게 변환할 수 있도록 다음과 같은 윈속 함수가 제공된다.

/* IPv4 주소 변환 */

// 문자열 → 숫자
unsigned long	inet_addr(const char *cp);

// 숫자 → 문자열
char	*inet_ntoa(struct in_addr in);
  • inet_addr: 문자열 형태로 IPv4 주소를 입력받아 32비트 숫자(네트워크 바이트 정렬)로 리턴
  • inet_ntoa: 32비트 숫자(네트워크 바이트 정렬)로 IPv4 주소를 입력 받아 문자열 형태로 리턴

WSAStringToAddress, WSAAddressToString

IPv4와 IPv6 주소 변환을 모두 지원하는 함수도 제공된다. 단, 윈도우 XP는 SP1 이상에서 IPv6 프로토콜을 설치해야 IPv6 주소 변환이 가능하다.

/* IPv4 또는 IPv6 주소 변환 */

// 문자열 → 숫자
int WSAStringToAddress (
	LPTSTR				AddressString,	// 문자열 형식의 IP 주소
    INT					AddressFamily,	// AF_INET 또는 AF_INET6
    LPSWAPROTOCOL_INFO	lpProtocolInfo,	// NULL
    LPSOCKADDR			lpAddress,		// IP 주소(숫자)를 저장할 구조체: SOCKADDR_IN 또는 SOCKADDR_IN6
    LPINT				lpAddressLength	// 주소 구조체의 길이
);

// 숫자 → 문자열
int WSAAddressToString (
	LPSOCKADDR			lpsaAddress,	 		// 숫자 형식의 IP 주소: SOCKADDR_IN 또는 SOCKADDR_IN6
    DWORD 				dwAddressLength,		// 주소 구조체의 길이
    LPWSAPROTOCOL_INFO	lpProtocolInfo,			// NULL
    LPTSTR				lpAddressString,		// IP 주소(문자열)을 저장할 버퍼
    LPDWORD				lpdwAddressStringLength	// 버퍼의 길이
);

바이트 정렬 함수와 IP 변환 함수 사용 예

다음은 바이트 정렬 함수와 IP 주소 변환 함수를 SOCKADDR_IN 구조체에 사용하는 예이다.

  1. 소켓 주소 구조체 초기화 및 소켓 함수에 넘겨준다.(SocketFunc()은 임의의 소켓 함수)

    // 소켓 구조체 초기화
    SOCKADDR_IN addr;
    ZeroMemory(&addr, sizeof(addr));	// 0으로 채움
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("123.456.789.0");	// 문자열을 32비트 IPv4 주소로 변환하여 넣음
    addr.sin_port = htons(9000);	// 네크워크 바이트 정렬을 하여 넣음
    
    // 소켓 함수 호출
    SocketFunc(..., (SOCKADDR *) &addr, sizeof(addr), ...);
  2. 소켓 함수가 소켓 주소 구조체를 입력으로 받아 내용을 채우면, 응용 프로그램이 이를 출력 등의 목적으로 사용(SocketFunc()은 임의의 소켓 함수)

    // 소켓 함수 호출
    SOCKADDR_IN addr;
    int addrlen = sizeof(addr);
    SocketFunc(..., (SOCKADDR *) &addr, &addrlen, ...);
    
    // 소켓 주소 구조체 사용
    printf("IP 주소=%s, 포트 번호=%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

IP 주소 변환 함수 주의점

  1. inet_addr() 함수는 이미 네트워크 바이트 정렬된 IP 주소를 리턴하기 때문에 htonl() 함수를 다음과 같이 적용하면 안된다.
    addr.sin_addr.s_addr = htonl(inet_addr("123.456.789.0"));	→ (X)
  2. addr.sin_addr은 이미 네트워크 바이트 정렬된 IP 주소이기 때문에 ntohl() 함수를 다음과 같이 적용하면 안된다.
    printf("IP 주소=%s, 포트 번호=%d\n", inet_ntoa(ntohl(addr.sin_addr(), ntohs(addr.sin_port)); → (X)

IP 주소 변환 함수 연습

IPv4와 IPv6 주소를 변환하여 화면에 출력하는 예제를 작성해 두었다. IP 주소 변환 함수를 모두 연습하기 위해 문자열을 숫자로, 숫자를 다시 문자열로 바꾸어 출력한다.

#pargma comment(lib, "ws2_32")
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	WSADATA wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    	return 1;
    
    /* IPv4 변환 연습 */
    
    // 원래 IPv4 주소 출력
    char *ipv4test = "123.456.789.0";
    printf("IPv4 주소(변환 전) = %s\n", ipv4test);
    
    // inet_addr() 함수 연습
    printf("IPv4 주소(변환 후) = 0x%x\n", inet_addr(ipv4test));
    
    // inet_ntoa() 함수 연습
    IN_ADDR ipv4num;
    ipv4num.s_addr = inet_addr(ipv4test);
    printf("IPv4 주소(다시 변환 후) = %s\n", inet_ntoa(ipv4num));
    printf("\n");
    
    /* IPv6 변환 연습 */
    
    // 원래 IPv6 주소 출력
    char *ipv6test = "0000:1234:abcd:ffab:0023:eb00:ffff:1111";
    printf("IPv6 주소(변환 전) = %s\n", ipv6test);
    
    // WSAStringToAddress() 함수 연습
    SOCKADDR_IN6 ipv6num;
    int addrlen = sizeof(ipv6num);
    WSAStringToAddress(ipv6test, AF_INET6, NULL, (SOCKADDR *) &ipv6num, &addrlen);
    printf("IPv6 주소(변환 후) = 0x");
    for (int i = 0; i < 16; i++)
    	printf("%02x, ipv6num.sin6_addr.u.Byte[i]);
    printf("\n");
    
    // WSAAddressToString() 함수 연습
    char ipaddr[50];
    DWORD ipaddrlen = sizdeof(ipaddr);
    WSAAddressToString((SOCKADDR *) &ipv6num, sizeof(ipv6num), NULL, ipaddr, &ipaddrlen);
    printf("IPv6 주소(다시 변환 후) = %s\n", ipaddr);
    
    WSACleanup()
    return 0;
}

실행 결과는 아래와 같다.

(실행 결과 그림)

참고 자료
김성우 저, "TCP/IP 윈도우 소켓 프로그래밍", 한빛아카데미, 2018

0개의 댓글