외로운 Nova의 작업실

c 소켓프로그래밍 공부 - 8(도메인으로 ip요청2) 본문

Programming/C

c 소켓프로그래밍 공부 - 8(도메인으로 ip요청2)

Nova_ 2022. 3. 25. 16:19

안녕하세요. 오늘은 어제 오류에대해서 공부하고 원래 하려했던 도메인네임으로 ip를 받아보도록 하겠습니다.

먼저 어제 오류는 WSA관련 함수를 사용하지않아서 나타나는 현상이였습니다.

그러니 socket프로그래밍을 winsock.h를 통해서 할땐 꼭 wsa관련함수를 작성해야 합니다.

하지만 어제 짯던 프로그램은 돌아가지 않았습니다.

먼저 코드를 보시죠

#include <stdio.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <stdint.h>
int main(int argc, char* argv[]) {

	WSADATA wsaData;

	struct addrinfo hint; //힌트를 담을 구조체 선언
	struct addrinfo* result; //결과값을 담을 구조체 포인터 선언
	int check = 0; //getaddrinfo함수의 반환형을 담을 변수
	int address = 0;

	WSAStartup(MAKEWORD(2, 2), &wsaData);

	memset(&hint, 0, sizeof(hint));
	hint.ai_family = AF_INET;
	hint.ai_socktype = SOCK_STREAM;

	check = getaddrinfo("www.naver.com", "80", &hint, &result);

	printf("naver의 ip주소 : %lu", ((SOCKADDR_IN*)(result->ai_addr))->sin_addr.s_addr);

	WSACleanup();
	freeaddrinfo(result);


}

아래는 실행 결과입니다.

이렇듯 엉뚱한 값이 들어가게됩니다.

이유는 사실 구조체 IN_ADDR의 멤버 s_addr은 ip주소를 빅엔디안 방식의 10진수로 가지고있습니다.

여기서부터는 어려운 내용이니 천천히 읽어주시면 감사하겠습니다.

우리는 ip주소를 문자열로 줍니다. 예를 들어 "192.168.0.1"로 말입니다.

그럼 저 문자열이 s_addr의 타입인 in_addr_t 타입인 unsigned long 4바이트 정수형으로 어떻게 들어가는지 설명해보겠습니다.

먼저 컴퓨터는 문자열을 . 기준으로 4개의 정수로 나눕니다

192(배구십이) 168(백육십팔) 0(영) 1(일) 이런식으로 나눕니다.

이후 2진법으로 바꿉니다. 하지만 2진법은 사람이 인식하기 힘들기때문에 16진법으로 바꿔보도록 하겠습니다.

192 = 0xc0

168 = 0xa8

0 = 0x00

1 = 0x01

=> c0 a8 00 01

그리고 이 4개의 숫자를 빅엔디안 순서대로 정렬합니다.

=> 01 00 a8 c0

그리고 4개로 나눠져있는걸 하나로 합칩니다

=> 0100a8c0

그리고 16진법으로 표현된걸 다시 10진법으로 표현하면

=> 16820416이됩니다.

 

이런식으로 "192.168.0.1"이 16820416이라는 숫자로 변환되어서 s_addr에 저장됩니다.

이를 함수로 구현한게 inet_pton입니다. 

프로토콜(ip주소)를 네트워크정수로 바꾸는 함수가 바로 inet_pton이라는 것입니다.

 

이걸 왜 설명했냐면 저 위의 콘솔에 찍힌 1606648543이란 숫자를 위의 방식의 거꾸로하면

16진수로 표현하면 5FC382DF입니다.

이를 5F C3 82 DF 라는 4개의 숫자로 나누고 10진수로 각각 변환하면

5F = 95

C3 = 195

82 = 130

DF = 223

이를 순서대로 다시 쓰면 95 195 130 223입니다.

이는 빅엔디안 방식으로, 사용자입장에서의 리틀엔디안방식으로 순서를 정렬하면 223 130 195 95입니다.

이를 .을 붙여서 문자열로 표현하면 "223.130.195.95"입니다.

이를 직접 URL에 80번포트로 접속하면 네이버가 접속이 됩니다.

 

위의 방식을 함수로 표현한것이 바로 inet_ntop입니다.

아래는 위의 방식을 적용해서 ip주소를 출력하는 코드입니다.

#include <stdio.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#include <string.h>
#include <stdint.h>
int main(int argc, char* argv[]) {

	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	struct addrinfo hint; //힌트를 담을 구조체 선언
	struct addrinfo* result; //결과값을 담을 구조체 포인터 선언
	SOCKADDR* sockAddr; //result로부터 SOCKADDR구조체 포인터를 받을 변수
	IN_ADDR inAddr;
	int check = 0; //getaddrinfo함수의 반환형을 담을 변수
	char str[1024]; //inet_ntop함수에서 받아낼 변수

	memset(&hint, 0, sizeof(hint));
	hint.ai_family = AF_INET;
	hint.ai_socktype = SOCK_STREAM;

	check = getaddrinfo("www.naver.com", "80", &hint, &result);

	sockAddr = result->ai_addr;
	inAddr = ((SOCKADDR_IN*)sockAddr)->sin_addr;

	inet_ntop(AF_INET, &(inAddr.s_addr), str, sizeof(str));

	printf("%s", str);

	WSACleanup();
	freeaddrinfo(result);


}

아래는 실행 사진입니다.

위와 다른 ip주소가 나오는건 접속수가 많은 도메인에서는 여러개의 ip를 두고 있어서 그렇습니다.

실제로 우의 223.130.200.104 80번 포트에 접속하면 네이버가 뜹니다.

이로써 6장에 이어 7장에서 도메인네임을 이용해 ip주소를 출력하는 코드를 작성해보았습니다.

정리하자면 인간이 기억하는건 도메인네임, 그래도 인간수준에서 컴퓨터에게 줄수있는건 빅엔디안방식으로 표현된 하나의 16진수를 담고있는 하나의 10진수 정수, 컴퓨터가 사용하는건 2진수의 ip주소입니다.

이는 이해하기 좀 어렵고 헷갈리는 내용이니 천천히 읽어보시면서 관련자료들을 찾아볼것을 권해드립니다.

 

c언어의 소켓프로그래밍은 대체로 복잡한 것같다. sockaddr의 대체를 위해 sockaddr_in이 있는것, 소켓프로그래밍을 위한 자료형이 따로 있는것, 구조체도 여러가지고 함수도 여러가지이다.

역시 네트워크를 다루는 건 어려운것같다.

그래도 컴퓨터의 꽃은 네트워크이니, c언어 수준의 네트워크를 꼭 다 배우고 말것이다.

node.js , Django 등 웹사이트 프레임워크(네트워크 프레임워크)는 많지만 다 c언어 수준의 프로그램으로 작성한 프레임워크다보니 c언어수준의 네트워크를 알고있으면 사용하기 편하다는 장점이있을것이다.

 

장정 10시간동안 삽질한 끝에 알게된 정보지만 노력한 만큼 값진 지식이다.

 

Comments