早期使用 gethostbyname() 做 DNS 查詢,再手工填充 sockaddr_in,僅支援 IPv4,程式碼繁瑣且不可移植。
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int getaddrinfo(const char *node, // 主機名或 IP
const char *service, // 埠號或服務名("http", "80")
const struct addrinfo *hints,
struct addrinfo **res); // 返回鏈表頭
struct addrinfo {
int ai_flags;
int ai_family; // AF_INET, AF_INET6, AF_UNSPEC
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
int ai_protocol; // 0, IPPROTO_TCP, IPPROTO_UDP
size_t ai_addrlen; // ai_addr 大小
struct sockaddr *ai_addr; // 位址結構
char *ai_canonname; // 規範主機名
struct addrinfo *ai_next; // 下一個節點
};
struct addrinfo hints, *servinfo;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // 自動填入本機 IP
getaddrinfo(NULL, "3490", &hints, &servinfo);
// servinfo 指向鏈表,遍歷嘗試 socket/bind/listen
freeaddrinfo(servinfo); // 釋放鏈表
struct addrinfo hints, *servinfo;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
// 無 AI_PASSIVE
getaddrinfo("www.example.net", "3490", &hints, &servinfo);
// 遍歷鏈表嘗試 socket/connect

// 遍歷 servinfo 鏈表
for (struct addrinfo *p = servinfo; p != NULL; p = p->ai_next) {
void *addr;
char ipstr[INET6_ADDRSTRLEN];
if (p->ai_family == AF_INET) {
addr = &((struct sockaddr_in*)p->ai_addr)->sin_addr;
} else {
addr = &((struct sockaddr_in6*)p->ai_addr)->sin6_addr;
}
inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
printf("%s\n", ipstr);
}
gai_strerror() 取得錯誤字串。freeaddrinfo() 釋放記憶體。AI_PASSIVE 僅用於伺服器端綁定通配位址。