分類: 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);
}