😁博客主頁😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客內容🤑:🍭嵌入式開發、Linux、C語言、C++、數據結構、音視頻🍭
🤣本文內容🤣:🍭介紹 getaddrinfo 函數 🍭
😎金句分享😎:🍭你不能選擇最好的,但最好的會來選擇你——泰戈爾🍭
?發布時間?:2024-03-01 14:15:54
本文未經允許,不得轉發!!!
目錄
- 🎄一、概述
- 🎄二、getaddrinfo 函數
- ?2.1 getaddrinfo 函數介紹
- ?2.2 struct addrinfo 結構體說明
- 🎄三、gai_strerror、freeaddrinfo 函數
- ?3.1 gai_strerror 函數介紹
- ?3.2 freeaddrinfo 函數介紹
- 🎄四、getaddrinfo 函數使用例子
- 🎄五、總結
🎄一、概述
前面介紹過域名和IP地址之間轉換的兩個函數:gethostbyname
和gethostbyaddr
,但是這兩個函數僅僅支持IPv4。本文再介紹一個可支持 IPv4 和 IPv6 的函數getaddrinfo
,該函數可以處理名字到地址以及服務到端口這兩種轉換。
🎄二、getaddrinfo 函數
?2.1 getaddrinfo 函數介紹
-
1、函數原型:
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints, struct addrinfo **res); void freeaddrinfo(struct addrinfo *res); const char *gai_strerror(int errcode);
-
2、函數描述:
getaddrinfo
函數根據給定的主機名和服務名,返回一個struct addrinfo
結構鏈表,每個struct addrinfo
結構都包含一個互聯網地址。getaddrinfo
函數將gethostbyname
和getservbyname
函數提供的功能組合到一個接口中,但與后一個函數不同,getaddrinfo
是可重入的,可支持IPv4、IPv6。 -
3、函數參數:
-
node:一個主機名或地址串( IPv4的點分十進制數串或IPv6的十六進制數串)。如果
hints.ai_flags
包含AI_NUMERICHOST標志,則此參數必須是IP地址字符串; -
service:一個服務名或十進制端口號數串。如果此參數被設置為一個服務名稱,則會將其轉換為相應的端口號。如果設置為NULL,則返回的套接字地址的端口號將保持未初始化狀態。如果在hints.ai_flags中指定了AI_NUMERICSERV,并且此參數不為NULL,則此參數必須指向包含數字端口號的字符串;
-
hints:hints參數可以是一個空指針,也可以是一個指向某個addrinfo結構的指針,調用者在這個結構中填入關于期望返回的信息類型的暗示。hints參數中,調用者可以設置的字段有:ai_flags、ai_family、ai_socktype、ai_protocol。其中,ai_flags取值如下表:
取值 說明 AI_PASSIVE 套接字將用于被動打開. AI_CANONNAME 告知getaddrinfo函數返回主機的規范名字. AI_NUMERICHOST 防止任何類型的名字到地址映射,hostname參數必須是一個地址串。 AI_NUMERICSERV 防止任何類型的名字到服務映射, service參數必須是一個十進制端口號。AI__V4MAPPED AI_V4MAPPED 如果同時指定ai_family成員的值為AF_INET6,那么如果沒有可用的AAAA記錄,就返回與A記錄對應的IPv4映射的IPv6地址。 AI_ALL 如果同時指定AI_V4MAPPED標志,那么除了返回與AAAA記錄對應的IPv6地址外,還返回與A記錄對應的IPv4映射的IPv6地址。 AI_ADDRCONFIG 按照所在主機的配置選擇返回地址類型,也就是只查找與所在主機回饋接口以外的網絡接口配置的IP地址版本一致的地址。 ai_family 取值一般為
AF_XXX
,例如:AF_INET
、AF_INET6
、AF_UNSPEC
(不限制IP地址協議);
ai_socktype 取值一般為SOCK_XXX
,例如:SOCK_STREAM
、SOCK_DGRAM
。
ai_protocol 字段指定返回的套接字地址的協議。在該字段中指定0表示getaddrinfo函數可以返回具有任何協議的套接字地址。 -
res:傳出參數,如果本函數返回成功0,則 res 參數指向的變量已被填入一個指針,它指向的是由其中的 ai_next 成員串接起來的 addrinfo 結構鏈表。
-
-
4、返回值:
成功返回0,失敗返回非0,取值如下表:常值 說明 EAI_AGAIN 名字解析中臨時失敗 EAI_BADFLAGS ai_flags的值無效 EAI_FAIL 名字解析中不可恢復地失敗 EAI_FAMILY 不支持ai_family EAI_MEMORY 內存分配失敗 EAI_NONAME hostname或service未提供,或者不可知 EAI_OVERFTOW 用戶參數緩沖區溢出(僅限getnameinfo( )函數) EAI_SERVICE 不支持ai_socktype類型的service EAI_SOCKTYPE 不支持ai_socktype EAI_SYSTEM 在errno變量中有系統錯誤返回
?2.2 struct addrinfo 結構體說明
struct addrinfo
結構體定義在頭文件 netdb.h
中,結構體聲明如下:
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;
};
結構體字段說明:
- ai_flags:標志,在調用時使用,具體取值見上面
hints參數
的說明; - ai_family:IP協議族,一般取值有
AF_INET
、AF_INET6
、AF_UNSPEC
(不限制IP地址協議); - ai_socktype:socket類型,取值一般為
SOCK_XXX
,例如:SOCK_STREAM
、SOCK_DGRAM
; - ai_protocol:此字段指定返回的套接字地址的協議,一般有
IPPROTO_UDP
、IPPROTO_TCP
- ai_addrlen:返回的地址結構體
ai_addr
的長度。一般IPv4是4,IPv6是16; - ai_addr:存儲IP地址數據,一般轉換成
struct sockaddr_in
或struct sockaddr_in6
使用; - ai_canonname:正式的、標準的名稱;
- ai_next:用作鏈表結點指針,指向下一個
struct addrinfo
結點。
🎄三、gai_strerror、freeaddrinfo 函數
在使用 getaddrinfo 函數時,還有兩個函數也會使用到,下面簡單介紹一下這兩個函數。
?3.1 gai_strerror 函數介紹
- 1、函數原型:
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> const char *gai_strerror(int errcode);
- 2、函數描述:
getaddrinfo出錯會返回的非0錯誤值,gai_strerror以這些值為它的唯一參數,返回一個指向對應的出錯信息串的指針。常值 說明 EAI_AGAIN 名字解析中臨時失敗 EAI_BADFLAGS ai_flags的值無效 EAI_FAIL 名字解析中不可恢復地失敗 EAI_FAMILY 不支持ai_family EAI_MEMORY 內存分配失敗 EAI_NONAME hostname或service未提供,或者不可知 EAI_OVERFTOW 用戶參數緩沖區溢出(僅限getnameinfo( )函數) EAI_SERVICE 不支持ai_socktype類型的service EAI_SOCKTYPE 不支持ai_socktype EAI_SYSTEM 在errno變量中有系統錯誤返回
?3.2 freeaddrinfo 函數介紹
-
1、函數原型:
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> void freeaddrinfo(struct addrinfo *res);
-
2、函數描述:
由getaddrinfo返回的所有存儲空間都是動態獲取的(譬如來自malloc調用),包括addrinfo結構、ai_addr結構和ai_canonname字符串。這些存儲空間需要通過調用freeaddrinfo
返還給系統。 -
3、參數
res:res參數應指向由getaddrinfo返回的第一個addrinfo結構。這個鏈表中的所有結構以及由它們指向的任何動態存儲空間(譬如套接字地址結構和規范主機名)都被釋放掉。 -
4、注意:
如果getaddrinfo成功返回后,我們為了保存返回的信息而僅僅復制了返回的addrinfo結構,在調用freeaddrinfo后,就會存在一個錯誤
:我們前面復制的addrinfo結構中的指針指向的內存空間已在調用freeaddrinfo后返還給系統。
這種只復制結構體而沒復制結構體字段指向的內容的方式稱為淺復制
;復制結構體又復制結構體字段指向的內容的方式稱為深復制
。上面例子中,如果確實要保存信息,可以使用深復制來保存。
🎄四、getaddrinfo 函數使用例子
// getaddrinfo_sample.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>int main(int argc, char *argv[]) {struct addrinfo hints, *result, *rp;// 定義addrinfo結構體變量int err; // getaddrinfo函數返回值char ipstr[INET6_ADDRSTRLEN]; // 存儲IP地址字符串的緩沖區if (argc != 2) { // 檢查命令行參數數量是否正確fprintf(stderr, "Usage: %s hostname\n", argv[0]);return -1;}memset(&hints, 0, sizeof(hints)); // 初始化hints結構體hints.ai_family = AF_UNSPEC; // 不限制IP地址版本hints.ai_socktype = SOCK_STREAM; // 使用TCP協議if ((err = getaddrinfo(argv[1], NULL, &hints, &result)) != 0) { // 解析主機名并將結果存儲在result指針中fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(err));return -1;}printf("IP addresses for %s:\n", argv[1]);for (rp = result; rp != NULL; rp = rp->ai_next) { // 遍歷result指針中的所有套接字地址結構void *addr;char *ipver;if (rp->ai_family == AF_INET) { // IPv4地址struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;addr = &(ipv4->sin_addr);ipver = "IPv4";} else { // IPv6地址struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;addr = &(ipv6->sin6_addr);ipver = "IPv6";}inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr)); // 將套接字地址結構轉換為IP地址字符串printf(" %s: %s\n", ipver, ipstr); // 打印IP地址和版本號}freeaddrinfo(result); // 釋放由getaddrinfo函數分配的內存return 0; // 程序正常退出
}
運行結果:
下面是分別查詢www.baidu.com
(百度)、www.goolge.com
(谷歌)、blog.csdn.net
(CSDN)、localhost
(本地主機名)、ip6-localhost
、ip6-localnet
的打印結果。
localhost
(本地主機名)、ip6-localhost
、ip6-localnet
,這三個在/etc/hosts
文件中有說明,可以發現與查詢的一致。
🎄五、總結
👉本文重點介紹了 getaddrinfo、freeaddrinfo、gai_strerror 三個函數,并給出C語言使用例子。
通過本文的介紹,我們深入探討了 Linux 系統中 getaddrinfo 函數的定義和使用場景。getaddrinfo 函數在網絡編程中扮演著重要角色,允許開發人員根據主機名和服務名動態獲取地址信息,為構建靈活且健壯的網絡應用提供了便利。希望本文能幫助您更好地了解并應用 getaddrinfo 函數。
如果文章有幫助的話,點贊👍、收藏?,支持一波,謝謝 😁😁😁
參考資料:
1、Linux的man手冊
2、《Unix網絡編程卷1》