getaddrinfo()함수는 domain address를 받아서 네트워크 주소 정보(IP address)를 가져오는 함수이다.
예를 들어, "http://www.google.co.kr" 라는 domain address가 있는데, 이 주소는 사람이 알아보기 쉬운 주고이긴 하지만, 컴퓨터는 이 주소를 가지고 해당되는 구글의 서버를 찾아가지 못한다. 그래서 이 domain address와 대응되는 IP주소가 무엇인지를 알아 낸 뒤에 그 IP주소로 연결을 해야한다.
즉, Domain address -> IP address 변환을 하고 싶을 때 사용하는 함수라는 뜻이다. 이걸 전문용어로 DNS (Domain Name System/Service) resolving 이라고 한다.
그 중, 1~3번째는 입력 매개변수이고, 4번째 매개변수는 결과를 사용자에게 돌려주는 출력 매개변수이다.
결과는 addrinfo 구조체(strcut addrinfo) 의 linked list로 돌려준다.
이 결과는 사용을 끝낸 뒤엔 freeaddrinfo 함수로 메모리 해제를 해주어야 한다. 그렇지 않으면 메모리 누수가 발생한다.
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *host, // e.g. "www.example.com" or IP
const char *service, // e.g. "http" or port number
const struct addrinfo *hints,
struct addrinfo **result);
차근차근 계속 알아가보자.
함수 에러 시 리턴하는 에러값들은 아래와 같이 다양하다. 상세 정보를 정리해준 블로그 주소(https://techlog.gurucat.net/293)다.
EAI_ADDRFAMILY, EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NODATA, EAI_NONAME, EAI_SERVICE, EAI_SOCKET, EAI_SYSTEM 등..
아래와 같이 사용하며, 사람이 읽을 수 있는 문자열로 에러값을 해석(변환)해준다.
const char *gai_strerror(int errcode);
getaddrinfo()함수의 인자로 들어가는 구조체 중 addrinfo가 있는데, 아래와 같이 정의된다.
"netdb.h" 헤더파일에 정의돼있다.
struct addrinfo {
int ai_flags; /* 추가적인 옵션을 정의 할 때 사용, 여러 flag를 bitwise OR-ing 하여 넣는다.*/
int ai_family; /* address family를 AF_INET등과 같이 나타낸다. */
int ai_socktype; /* 소켓 타입으로 SOCK_STREAM등과 같이 나타낸다. */
int ai_protocol; /* 0혹은 IPv4와 IPv6에 대한 IPPROTO_XXX와 같은 값을 가진다. */
char *ai_canonname; /* host의 canonical name을 나타낸다. */
size_t ai_addrlen; /* 소켓 주소인 ai_addr 길이를 나타낸다. */
struct sockaddr *ai_addr; /* ai_addr은 소켓 주소 구조체를 가리키는 포인터다. */
struct addrinfo *ai_next; /* addrinfo는 링크드 리스트로, 다음 데이터 포인터를 가리킨다. */
};
getaddrinfo()에서 hints 인자를 넣을 때, 위의 int형 4개 필드에만(최대 4개) 값을 지정해준다. 나머지는 0(또는 NULL)이어야 한다. 실제 우리는 memset을 활용해서 전체 구조체를 0으로 설정하고, 4개 이하의 필드에만 값을 지정해준다.
어떤 운영체제에서는 size_t가 아닌 socklen_t를 ai_addrlen으로 사용하기도 한다.
대부분의 소켓함수에서 두 type은 호환되지만, 호환불가능한 경우 런타임 오류룰 발생시키는 경우가 있다.
예를 들면, big-endian을 사용하는 64-bit Solaris 9 시스템은 size_t는 8바이트, socketlen_t는 4바이트 이므로 두 type간 호환되지 않고 런타임 오류 발생할 것이다.
따라서 본인이 작성한 코드가 어떤 시스템 위에서 동작할 지를 충분히 고려해야 한다.
(출처: https://techlog.gurucat.net/294)
www.google.com의 80포트 네트워크 주소를 얻는 예제다. (출처 블로그는 위와 같다.)
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc,char *argv[])
{ int status;
struct addrinfo hints;
struct addrinfo *servinfo; // 결과를 저장할 변수
memset(&hints, 0, sizeof(hints)); // hints 구조체의 모든 값을 0으로 초기화
hints.ai_family = AF_UNSPEC; // IPv4와 IPv6 상관하지 않고 결과를 모두 받겠다
hints.ai_socktype = SOCK_STREAM; // TCP stream socket
status = getaddrinfo("www.google.com", "80", &hints, &servinfo);
}