getaddrinfo()

getaddrinfo()

int getaddrinfo(const char *domain, 
                const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);
struct addrinfo {
    int              ai_flags;
    int              ai_family;
    int              ai_socktype;
    int              ai_protocol;
    socklen_t        ai_addrlen;
    struct sockaddr *ai_addr;
    char            *ai_canonname;
    struct addrinfo *ai_next;
};
  • 查詢給定的域名 domain,與服務名 service,並返回對應的IP地址與端口號.shell

  • domain;能夠是域名,也能夠是IP地址的點分十進制表示,或者是0;app

  • service;能夠是服務名,如"http","ftp";也能夠是端口號的十進制字符串,如"80","23";或者是0dom

    • 若取0,則查詢結果中的端口號是未設置的,此時還需手動賦值!
      spa

  • hints,指向着一個 addrinfo 結構體,該結構體除了如下指定的域外,其餘域應該取值爲0,或者NULL;指針

    • ai_family;用來限定 getaddrinfo() 的查詢結果中 ai_family 的取值;能夠是 AF_INET(此時 getaddrinfo() 查詢結果中 ai_family 的取值只能是 AF_INET),或者 AF_INET6,或者 AF_UNSPEC(不限定查詢結果中ai_family的取值);
      code

    • ai_socktype;用來限定查詢結果中 ai_socktype 的取值;能夠是 SOCK_STREAM,SOCK_DGRAM;或者爲0,表示不限定;
      orm

    • ai_protocol;用來限定查詢結果中 ai_protocol 的取值;若爲0,則表示不限定
      ip

    • ai_flags;位掩碼,能夠是如下值,或者它們或運算的結果:ci

      • AI_NUMERICHOST;此時代表 domain 是IP地址的點分十進制;不是域名,此時不會執行DNS查詢.
        字符串

      • AI_NUMERICSERV;此時代表 service 只是端口號的字符串表示;不是服務名,

      • AI_PASSIVE;僅當 domain == 0 時起做用;當 domain==0 時,若設置了該標誌,則查詢結果中的IP地址是通配地址;若未設置該標誌,則IP地址是環回地址.

      • AI_CANONNAME;若設置了該標誌,則 *res->ai_canonname 域中存放着目的主機(即 domain 指定的主機)的 official name(我理解爲徹底限定域名).

      • AI_ADDRCONFIG;若設置了該標誌,則僅當本地主機至少配置了一個IPV4(或IPV6)地址時,纔會在返回的查詢結果中包含IPV4(或IPV6)地址;以下:

域名  對應着以下 IP 地址:
    173.194.127.180
    173.194.127.176
    2404:6800:4005:802::1010
若本地主機僅配置了 IPV4 地址,則返回的查詢結果中不包含 IPV6 地址,即此時只有:
    173.194.127.180
    173.194.127.176
一樣若本地主機僅配置了 IPV6 地址,則返回的查詢結果中僅包含IPV6地址.
    • 若 hints==0,則至關於 ai_family==AF_UNSPEC,ai_socktype==0,ai_protocol==0,ai_flags==AI_ADDRCONFIG|AI_V4MAPPED.

  • res;getaddrinfo() 返回的查詢結果是一個單鏈表,鏈表中每個元素類型爲 struct addrinfo;鏈表的第一個元素的指針存放在 *res 中.

  • 返回值;若不爲0,則代表查詢出錯,此時返回值表示出錯碼;返回0,表示查詢成功.

ByteArray family_to_str(int family){
    if(family == AF_INET)
        return "AF_INET";
    if(family == AF_INET6)
        return "AF_INET6";
    ByteArray result("Unknow");
    result.appendFormat("(%d)",family);
    return result;
}

ByteArray socktype_to_str(int socktype){
    if(socktype == SOCK_STREAM)
        return "SOCK_STREAM";
    if(socktype == SOCK_DGRAM)
        return "SOCK_DGRAM";
    ByteArray result("Unknow");
    result.appendFormat("(%d)",socktype);
    return result;
}

ByteArray procotol_to_str(int proto){
    if(proto == IPPROTO_TCP)
        return "TCP";
    if(proto == IPPROTO_UDP)
        return "UDP";
    ByteArray result("Unknow");
    result.appendFormat("(%d)",proto);
    return result;
}

void print_addrinfo(const struct addrinfo *addr_info){
    printf("faimly: %s\nsocktype: %s\nprotocol: %s\ncannoname: %s\n"
        ,family_to_str(addr_info->ai_family).constData()
        ,socktype_to_str(addr_info->ai_socktype).constData()
        ,procotol_to_str(addr_info->ai_protocol).constData()
        ,addr_info->ai_canonname);

    if(addr_info->ai_addr->sa_family == AF_INET){
        struct sockaddr_in *addr_ptr = (struct sockaddr_in*)addr_info->ai_addr;
        char buf[INET_ADDRSTRLEN];
        if(inet_ntop(AF_INET,&addr_ptr->sin_addr,buf,INET_ADDRSTRLEN) == 0)
            SystemCallFailed(inet_ntop);
        printf("ipv4: %s\nport: %d\n",buf,ntohs(addr_ptr->sin_port));
    }else if(addr_info->ai_addr->sa_family == AF_INET6){
        struct sockaddr_in6 *addr_ptr = (struct sockaddr_in6*)addr_info->ai_addr;
        char buf[INET6_ADDRSTRLEN];
        if(inet_ntop(AF_INET6,&addr_ptr->sin6_addr,buf,INET6_ADDRSTRLEN) == 0)
            SystemCallFailed(inet_ntop);
        printf("ipv6: %s\nport: %d\n",buf,ntohs(addr_ptr->sin6_port));
    }else
        printf("ip: Unknow\nport: Unknow\n");
}

int main(int argc,char *argv[]){
    if(argc < 3){
        printf("%s 域名/IP地址 服務名/端口號\n",argv[0]);
        return 1;
    }
    int core_ret;
    struct addrinfo *result;
    struct addrinfo hints;
    memset(&hints,0,sizeof(hints));
    hints.ai_flags = AI_CANONNAME;
    core_ret = getaddrinfo(argv[1],argv[2],&hints,&result);
    if(core_ret != 0)
        ThrowException(0,"func=getaddrinfo;ret_code=%d;err_str=%s",core_ret,gai_strerror(core_ret));
    while(result){
        print_addrinfo(result);
        putchar('\n');
        result = result->ai_next;
    }
    return 0;
}
# 程序運行結果
# 此時域名 m 解析後獲得一個 IPV4 地址.服務名 http 解析後獲得一個端口號,但支持 TCP,UDP 2種協議.因此總可能數:1(1種IP地址)*2(2種協議)=2 
# 將根據解析出的IP地址的類型設置 ai_family 域(即若IP地址是IPV4地址,則爲 AF_INET,若爲IPV6地址,則設置爲 AF_INET6).
# 根據服務名支持的協議設置 ai_socktype,ai_protocol;
$ ./Test www.so.com http
faimly: AF_INET 
socktype: SOCK_STREAM
protocol: TCP
cannoname: so.qh-lb.com
ipv4: 101.4.60.8
port: 80

faimly: AF_INET
socktype: SOCK_DGRAM
protocol: UDP
cannoname: (null)
ipv4: 101.4.60.8
port: 80

#此時域名  解析後獲得一個 IPV4 地址;服務名 ftp 解析後獲得一個端口號,而且只支持 TCP 協議,因此總可能數: 1*1=1;
$ ./Test www.360.cn ftp
faimly: AF_INET
socktype: SOCK_STREAM
protocol: TCP
cannoname: www.360.cn
ipv4: 101.4.60.193
port: 21

freeaddrinfo()

void freeaddrinfo(struct addrinfo *res);
  • 釋放 getaddrinfo() 返回的單鏈表.

gai_strerror()

const char *gai_strerror(int errcode);
相關文章
相關標籤/搜索