처음부터 차근차근

바이트 순서 변환과 호스트 정보 송수신 관련 함수들 본문

Linux & Unix

바이트 순서 변환과 호스트 정보 송수신 관련 함수들

_soyoung 2022. 5. 14. 22:04
반응형

endian

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
int main(void) 
{
	int nValue = 0x12345678; // int형 변수 선언
	printf("Value : 0x%08X\n", nValue); // nValue값 출력
	for (int i = 0; i < 4; i++) {
		// 각 주소에 있는 값 출력
		// 순서가 거꾸로 나온다! -> little-endian은 이런 식으로 거꾸로 정리가 되서 관리된다.
		char* pC = (char*)&nValue + i;
		printf("%p : 0x%02X\n", pC, *pC);
	}
	printf("\n");// 개행
	return 0; // 프로그램 종료
}

 

실행결과

각 주소에 있는 값을 출력했을 때 순서가 거꾸로 출력되는 것을 볼 수 있다.

Little-endian은 이런 식으로 거꾸로 정리되어 관리된다.

 

 

 

Endian_htonl

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
#include <arpa/inet.h> 
int main(void)
{
	// 변수 선언
	int nValue = 0x12345678; 
	uint32_t nNetValue = 0;
	printf("nValue : 0x%08X\n", nValue); // nValue값 출력
	nNetValue = htonl(nValue);  // 네트워크 형태의 값으로 변경
	printf("nNetValue : ox%08X\n", nNetValue); // nNetValue 값 출력
	for (int i = 0; i < 4; i++) {
		// 각 주소에 있는 nNetValue 값 출력
		char* pC = (char*)&nNetValue + i;
		printf("%p : 0x%02X\n", pC, *pC);
	}
	return 0; // 프로그램 종료
}

 

실행결과

변형이 된 값인 nNetValue는 순서가 바이트단위로 뒤바뀌어 출력된 것을 볼 수 있고,

각 주소에 있는 값을 보면 nNetValue의 순서대로(12, 34…) 출력되는 것을 볼 수 있다.

 

 

Inet_addr

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
#include <arpa/inet.h> // inet_addr() 함수 사용할 때 필요

// argc : 명령 파라미터의 개수(자기 자신까지 1개로 침)
// argv[] : 각각의 아규멘트의 값들(argv[0]는 자기자신)
int main(int argc, char* argv[])
{
	// 변수 선언
	in_addr_t stAddr; // 정수형
	unsigned char* p = (unsigned char*)&stAddr;
	if (argc != 2) return -1;
	
	// 매개변수(ip주소) 출력
	printf("IP Adress is %s\n", argv[1]); 
	stAddr = inet_addr(argv[1]); // big-endien로 변환된 값
	
	printf("IP Adress is 0x%X\n", stAddr); // big-endien로 변환된 값 출력
	// 각 배열의 값 출력
	printf("\t[0]=0x%02X\n", p[0]);
	printf("\t[1]=0x%02X\n", p[1]);
	printf("\t[2]=0x%02X\n", p[2]);
	printf("\t[3]=0x%02X\n", p[3]);
	return 0; // 프로그램 종료
}

 

실행결과

0x100000F : 정수형(in_addr_t)으로 바뀐 것을 보면 순서가 반대로 된 것을 볼 수 있다.

(15 16진수로 표현하면 0F)

이렇게 거꾸로 나와있지만 배열 위치로 보면 순서가 제대로 잘 저장되어 있다.

 

 

Inet_network

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
#include <arpa/inet.h> // inet_network() 함수 사용할 때 필요

// argc : 명령 파라미터의 개수(자기 자신까지 1개로 침)
// argv[] : 각각의 아규멘트의 값들(argv[0]는 자기자신)
int main(int argc, char* argv[])
{
	// 변수 선언
	in_addr_t stAddr; // 정수형
	unsigned char* p = (unsigned char*)&stAddr; // 포인터로 stAddr 주소 가리킴

	// 명령 파라미터 개수가 2개가 아니면 프로그램 종료
	if (argc != 2) return -1;

	// 매개변수(ip주소) 출력
	printf("IP Adress is %s\n", argv[1]);
	stAddr = inet_network(argv[1]); 
	printf("Inet Adress is 0x%X\n", stAddr);// 매개변수로 넣은 값 출력

	// 각 배열의 값 출력
	printf("\t[0]=0x%02X\n", p[0]);
	printf("\t[1]=0x%02X\n", p[1]);
	printf("\t[2]=0x%02X\n", p[2]);
	printf("\t[3]=0x%02X\n", p[3]);

	return 0; // 프로그램 종료
}

 

실행결과

0xF000001 : 순서가 15.0.0.1과 동일하게 나오는 것을 볼 수 있다.

이렇게 동일하게 나오면 메모리 상에서는 거꾸로 기록된다.

그래서 배열의 값을 출력해봤을 때 거꾸로 출력되는 것을 볼 수 있다.

 

 

inet_aton

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
#include <arpa/inet.h> // inet_aton() 함수 사용할 때 필요

// argc : 명령 파라미터의 개수(자기 자신까지 1개로 침)
// argv[] : 각각의 아규멘트의 값들(argv[0]는 자기자신)
int main(int argc, char* argv[])
{
	// 변수 선언
	struct in_addr stAddr; 
	unsigned char* p = (unsigned char*)&stAddr; // 포인터로 stAddr 주소 가리킴

	// 명령 파라미터 개수가 2개가 아니면 프로그램 종료
	if (argc != 2) return -1;

	// 매개변수(ip주소) 출력
	printf("IP Adress is %s\n", argv[1]);

	// bin-endien로 변환
	if (inet_aton(argv[1], &stAddr) != 1) {// 1이 리턴되면 성공, 아니면 실패
		perror("Failed to convert the address.");
		return 0;
	}
	printf("Inet Adress is 0x%X\n", stAddr); // 매개변수로 넣은 값 변환해서 출력

	// 각 배열의 값 출력
	printf("\t[0]=0x%02X\n", p[0]);
	printf("\t[1]=0x%02X\n", p[1]);
	printf("\t[2]=0x%02X\n", p[2]);
	printf("\t[3]=0x%02X\n", p[3]);

	return 0; // 프로그램 종료
}

 

실행결과

0x100000F : 순서가 반대로 된 것을 볼 수 있다.

이렇게 거꾸로 나와있지만 배열 위치로 보면 순서가 제대로 잘 저장되어 있다.

 

 

Inet_ntoa

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
#include <arpa/inet.h> // inet_ntoa() 함수 사용할 때 필요
int main(void)
{
	// 변수 선언
	struct in_addr_t addr; // 정수형
	char *ip_addr;
	addr.s_addr = htonl(0x01020304); // 1234라는 값 대입
	printf("Network Adress is 0x%X\n", addr); // addr 출력
	// 네트워크 바이트 순서의 32비트 값을 dotted-decimal notation의 주소 값으로 변환
	ip_addr = inet_ntoa(addr); 
	printf("IP Adress is %s\n", ip_addr); // ip_addr 출력
	return 0; // 프로그램 종료
}

 

실행결과

Htonl 의해 01020304 거꾸로 출력되고,

inet_ntoa 의해 형식이 변환되어 1.2.3.4 출력되는 것을 있다.

 

 

Inet_ntoa2

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <unistd.h> // unix 계열 헤더
#include <arpa/inet.h> // inet_ntoa() 함수 사용할 때 필요

int main(void)
{
	// 변수 선언
	struct in_addr addr;
	struct in_addr addr2;
	char* ip_addr;
	char* ip_addr2;

	// big endian 방식으로 정렬 후 값 대입
	addr.s_addr = htonl(0x01020304);
	addr2.s_addr = htonl(0x12345678);

	// 값 대입한거 출력
	printf("Network Adress is 0x%X\n", addr);
	printf("Network Adress2 is 0x%X\n", addr2);

	// inet_ntoa 이용하여 dotted-decimal notation의 주소 값으로 변환
	ip_addr = inet_ntoa(addr);
	ip_addr2 = inet_ntoa(addr2);

	// 값 대입한거 출력
	printf("IP Adress is %s\n", ip_addr);
	printf("IP Adress2 is %s\n", ip_addr2);
	return 0; // 프로그램 종료
}

 

실행결과

0x4030201, 0x78563412 : htonl()으로 인해 값이 뒤집어져서 출력된다.

18.52.86.120 : Inet_ntoa()함수는 정적변수를 사용하기 때문에 adress adress2 서로 다른 값을 넣어도 같은 값이 출력된다.

 

 

Gethostbyname()

<print_hostent.h>

#include <netdb.h>
void print_hostent(struct hostent* host);

 

<print_hostent.c>

#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <netinet/in.h>
#include <arpa/inet.h> 
#include <sys/socket.h> 
#include "print_hostent.h"

void print_hostent(struct hostent* host) 
{
	int i;

	printf("Official name of host : %s\n\n", host->h_name); // 파라미터로 받은 호스트 네임 출력
	if (host->h_aliases[0] != NULL) // 별칭이 여러개 있으면
	{
		printf("--Aliases--\n");

		// 별칭 출력
		for (i = 0; host->h_aliases[i]; i++) 
		{
			printf("%02dth: %s\n", i + 1, host->h_aliases[i]);
		}
		printf("\n");
	}

	// IPv4인지 IPv6인지 출력
	printf("Adress type : %s\n\n",
		(host->h_addrtype == AF_INET) ? "AR_INET" : "AF_INET6");
	
	// ip 주소들 출력
	printf("IP Adress : \n");
	for (i = 0; host->h_addr_list[i]; i++) 
	{
		// inet_ntoa를 이용해 dotted-decimal notation의 주소 값으로 변환하여 출력
		printf("%02d : %s\n", i + 1,
			inet_ntoa(*(struct in_addr*)host->h_addr_list[i])); 
	}
	return;
}

 

<gethostbyname.c>

#include <unistd.h> // unix 계열 헤더
#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <netdb.h>
#include "print_hostent.h"
// argc : 명령 파라미터의 개수(자기 자신까지 1개로 침)
// argv[] : 각각의 아규멘트의 값들(argv[0]는 자기자신)
int main(int argc, char* argv[]) {
	struct hostent* host;
	// 파라미터 개수가 2개가 아니면
	if (argc != 2) {
		printf("usage : %s domain name\n", argv[0]);
		return -1; // 종료
	}
	// 첫 번째 파라미터인 도메인 명을 4바이트의 네트워크 주소로 변환 후 대입
	host = gethostbyname(argv[1]);
	
	// gethostbyname()에서 null이 리턴되면
	if (!host) 
	{
		printf("gethostbyname function error\n");
		return -1; // 종료
	}
	print_hostent(host); // 함수 호출
	printf("Done.\n");
	return 0; // 종료
}

 

실행결과

네이버를 파라미터로 넣었더니 네이버의 호스트 네임과 주소 타입, ip 주소가 출력되었다.

 

 

Gethostbyaddr

<gethostbyaddr.c>

#include <unistd.h> // unix 계열 헤더
#include <stdio.h> // c 언어 파일 작성할 때 필요한 헤더
#include <sys/socket.h> 
#include <netinet/in.h>
#include <arpa/inet.h> 
#include <netdb.h>
#include "print_hostent.h"

int main(int argc, char* argv[]) {
	// 변수 선언
	struct hostent* host;
	uint32_t addr = 0;

	// 파라미터 개수가 2개가 아니면
	if (argc != 2) 
	{
		printf("usage : %s domain name\n", argv[0]);
		return -1; // 종료
	}

	addr = inet_addr(argv[1]); //IP 주소를 네트워크 바이트 정렬 방식의 4바이트 정수로 변환해서 대입
	host = gethostbyaddr(&addr, 4, AF_INET); // 도메인명 얻음

	// null이 반환되면
	if (!host)
	{
		printf("gethostbyaddr function error\n");
		return -1;
	}
	print_hostent(host); // 함수 호출
	printf("Done.\n");
	return 0; // 종료
}

 

실행결과

네이버의 아이피 주소를 파라미터로 넣었더니 address type host name 등이 출력되었다.

 

 

 

출처 : 네트워크개론(22-1학기)김병국교수 강의 내용 변형 및 요약

반응형
Comments