getaddrinfo 使用詳解

 getaddrinfo使用詳解 2011-03-15 15:25:14

分類: LINUX

getaddrinfo是在gethostbyname系列函數不支持Ipv6的情況下逐漸催生的,其能夠處理名字到地址以及服務到端口這兩種轉換,返回一個sockaddr結構的鏈表,這些sockaddr地址結構隨後可有套接口函數(socket、bind、connect、listen等)直接調用,將協議相關性隱藏在該函數內部。應該儘量選擇使用getaddrinfo函數代替之前的getxx函數族,就像應該使用inet_ntop(inet_pton)代替inet_aton, inet_addr等函數一樣。

 

#include <netdb.h>

int getaddrinfo(const char* hostname, const char* service,

    const struct addinfo* hints, struct addrinfo** result);

其中hostname可以是主機名後者地址串(Ipv4點分十進制數串或者Ipv6十六進制數串);service參數是一個服務名或者十進制端口號數串。與getaddrinfo相關的系統配置文件包括/etc/hosts、/etc/services,用於處理主機名與地址串、服務名與端口號之間的轉換。

 

/etc/hosts存儲地址與主機名的對應關係,如下例:

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

10.0.1.73   fedora11

 

/etc/services 則存儲服務與端口號的對應關係,如下例:

tcpmux         1/tcp        # TCP port service multiplexer

tcpmux         1/udp       # TCP port service multiplexer

rje             5/tcp        # Remote Job Entry

rje             5/udp       # Remote Job Entry

echo           7/tcp        #TCP echo

echo           7/udp       #UDP echo

 

如果你的應用程序需要使用主機名代替IP地址,或服務名代替端口號,你需要先把對應關係增加到對應的配置文件中,否則getaddrinfo會解析出錯。

 

有了getaddrinfo就可以很方便的構建服務器及客戶端的應用程序,不用考慮數據尾端,地址轉換等。tcp_listen根據host及service的信息獲取sockaddr信息,創建套接字、綁定地址並監聽。同樣tcp_connect通過getaddrinfo返回的信息,連接服務器。創建udp的服務器與客戶端與此類似。

 

tcp_listen(const char* host, const char* serv)

{

    int listenfd, n;

    const int on = 1;

    struct addrinfo hints, *res, *ressave;

   

    bzero(&hints, sizeof(struct addrinfo));

    hints.ai_flags = AI_PASSIVE;

    hints.ai_family = AF_INET;

    hints.ai_socktype = SOCK_STREAM;

   

    if((n = getaddrinfo(host, serv, &hints, &res)) != 0) {

        printf("tcp_listen error for %s, %s: %s\n",

            host, serv, gai_strerror(n));

        return -1;

    }

   

    ressave = res;

    do {

        listenfd = socket(res->ai_family, res->ai_socktype,

                res->ai_protocol);

        if(listenfd < 0) {

            continue;

        }

       

        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

        if(bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) {

            break;

        }

       

        close(listenfd);

    }while((res = res->ai_next) != NULL);

   

    if(res == NULL) {

        printf("tcp_listen error for %s, %s: %s\n",

            host, serv, gai_strerror(n));

        return -1;

    }

   

    listen(listenfd, BACK_LOG);

   

    freeaddrinfo(ressave);

    return(listenfd);

}

 

int tcp_connect(const char* host, const char* serv)

{

    int sockfd, n;

    struct addrinfo hints, *res, *ressave;

   

    bzero(&hints, sizeof(struct addrinfo));

    hints.ai_family = AF_INET;

    hints.ai_socktype = SOCK_STREAM;

   

    if((n = getaddrinfo(host, serv, &hints, &res)) != 0) {

        printf("tcp_connect error for %s, %s: %s\n",

            host, serv, gai_strerror(n));

        return -1;

    }

   

    ressave = res;

    do {

        sockfd = socket(res->ai_family, res->ai_socktype,

                res->ai_protocol);

        if(sockfd < 0) {

            continue;

        }

       

        if(connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) {

            break;

        }

       

        close(sockfd);

    }while((res = res->ai_next) != NULL);

   

    if(res == NULL) {

        printf("tcp_connect error for %s, %s: %s\n",

            host, serv, gai_strerror(n));

        return -1;

    }

   

    freeaddrinfo(ressave);

    return(sockfd);

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章