函数原型:
- int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
-
void freeaddrinfo(struct addrinfo *res);
-
-
struct addrinfo
-
{ int ai_flags;
-
int ai_family;
-
int ai_socktype;
-
int ai_protocol;
-
size_t ai_addrlen;
-
struct sockaddr *ai_addr; /* 我觉得这个成员是这个函数最大的便利。 */
-
char *ai_canonname;
-
struct addrinfo *ai_next;
-
};
参数: node 即 主机名称 ,可以是主机名称字符串,比如“hostname", 也可以是IP地址的字符串,比如“ 1 92.169.1.1”
service 服务 即端口号 可以是一个服务的名称,比如"http", 也可以是一个数字字符串 ,比如“80” .
hints 可以理解为约束条件,即你创建的,要获得的addrinfo结构,有什么约束。 在hints中进行设置
res 很明显,这个就是我们最终获得的addrinfo结构。
这个函数产生原因。 以前版本里面需要套接字编程,要得到sockaddr 类型: 必须使用两种函数 getserverbyname (一类的函数) 和 gethostent (一类的函数), 然后构建 sockaddr_in 结构,在强制类型转换得到 sockaddr结构。 但是 getaddrinfo有了这个函数,这里过程就不用了,直接调用,就能得到 sockaddr 。 简化了编程过程。
注意点是: 这个函数,说起来,是get ,但是其实可以理解为creat 或者是理解为构建 。 因为你可以随意构建自己的地址结构addrinfo。
下面解释一下 hints 参数。 为什么要有这个参数 ?
看一个小程序:
- #include <stdio.h>
-
#include <stdlib.h>
-
#include <stdarg.h>
-
#include <string.h>
-
#include <fcntl.h>
-
#include <time.h>
-
#include <ctype.h>
-
#include <unistd.h>
-
#include <errno.h>
-
#include <sys/wait.h>
-
#include <signal.h>
-
#include <sys/types.h>
-
#include <netinet/in.h>
-
#include <arpa/inet.h>
-
#include <sys/stat.h>
-
#include <netdb.h>
-
#include <sys/socket.h>
-
void main()
-
{
-
struct addrinfo *ailist, *aip;
-
struct addrinfo hint;
-
struct sockaddr_in *sinp;
-
char *hostname = "luoyedeshan"; /* 这是我的用户名 */
-
char buf[INET_ADDRSTRLEN];
-
char *server = "3294"; /* 这是服务端口号 */
-
const char *addr;
-
int ilRc;
-
hint.ai_family = AF_UNSPEC; /* hint 的限定设置 */
-
hint.ai_socktype = 0; /* 这里可是设置 socket type . 比如 SOCK——DGRAM */
-
hint.ai_flags = AI_PASSIVE; /* flags 的标志很多 。常用的有AI_CANONNAME; */
-
hint.ai_protocol = 0; /* 设置协议 一般为0,默认 */
-
hint.ai_addrlen = 0; /* 下面不可以设置,为0,或者为NULL */
-
hint.ai_canonname = NULL;
-
hint.ai_addr = NULL;
-
hint.ai_next = NULL;
-
ilRc = getaddrinfo(hostname, server, &hint, &ailist);
-
if (ilRc < 0)
-
{
-
char str_error[100];
-
strcpy(str_error,gai_strerror(errno));
-
printf("str_error = %s", str_error);
-
return;
-
}
-
-
for (aip = ailist; aip != NULL; aip = aip->ai_next) /* 显示获取的信息 */
-
{
-
sinp = (struct sockaddr_in *)aip->ai_addr; /* 为什么是for 循环 ,先向下看 */
-
addr = inet_ntop(AF_INET, &sinp->sin_addr, buf, INET_ADDRSTRLEN);
-
printf(" addr = %s", addr?addr:"unknow ");
-
printf("port = %d ", ntohs(sinp->sin_port));
-
printf(" \n");
-
}
-
}
/* 执行结果如下 */
[luoyedeshan@luoyedeshan wqr]$ ./a.out
addr = 0.0.0.0 port = 3294
addr = 0.0.0.0 port = 3294
addr = 0.0.0.0 port = 3294
addr = 192.168.1.100 port = 3294
addr = 192.168.1.100 port = 3294
addr = 192.168.1.100 port = 3294
细心的你,也许发现,为什么重复了3次, 0.0.0.0 是本地地址,没话说, 192.168.1.100是局域网地址, 也没什么,为什么会得到3个重复的呢??
这就是 hint的作用,源代码里面我没甚至什么,hint都为空,所有有3个。 你在hint设置了,就可以屏蔽掉一些对你没用的addrinfo结构. 虽然有三个,他们的IP地址和端口号都一样,但是 这三个套接字类型不一样。 我试过 ,分别为 下面三种套接字类型 SOCK_DGRAM SOCK_SEQPACKET SOCK_STREAM .
getsockaddr这个函数的功能是将主机名映射成主机的地址,是个新的接口,以
取代以前的gethostbyname这个函数,因为后者不能处理ipv6的地址,并且以被标记为废弃,关于参数的细节有兴趣的朋友可以查帮助,我这里主
要说一下应该注意的问题,在使用中,最重要的是hint.ai_flags这个参数的设定问题,服务器端和客户端都分成两种情况,先说服务器端:
1,服务器端以服务的形式提供给用户,下面的代码是用到的库函数
代码:
- #include <sys/socket.h>
-
#include <sys/resource.h>
-
#include <sys/stat.h>
-
#include <errno.h>
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <signal.h>
-
#include <fcntl.h>
-
#include <syslog.h>
-
-
#define MAXSLEEP 128
-
-
int
-
connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
-
{
-
int nsec;
-
-
for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) {
-
if (connect(sockfd, addr, alen) == 0)
-
return(0);
-
if (nsec <= MAXSLEEP/2)
-
sleep(nsec);
-
}
-
return(-1);
-
}
-
-
int
-
initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
-
{
-
int fd;
-
int err = 0;
-
int reuse = 1;
-
-
if ((fd = socket(addr->sa_family, type, 0)) < 0)
-
return(-1);
-
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) {
-
err = errno;
-
goto errout;
-
}
-
if (bind(fd, addr, alen) < 0) {
-
err = errno;
-
goto errout;
-
}
-
if ((type == SOCK_STREAM) || (type == SOCK_SEQPACKET)) {
-
if (listen(fd, qlen) < 0) {
-
err = errno;
-
goto errout;
-
}
-
}
-
return(fd);
-
errout:
-
close(fd);
-
errno = err;
-
return(-1);
-
}
-
-
void
-
daemonize(const char *cmd)
-
{
-
int i, fd0, fd1, fd2;
-
pid_t pid;
-
struct rlimit rl;
-
struct sigaction sa;
-
-
umask(0);
-
-
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
-
printf("getrlimit failed");
-
exit(1);
-
}
-
-
if ((pid = fork()) < 0) {
-
printf("first fork failed");
-
exit(1);
-
} else if (pid > 0) {
-
exit(0);
-
}
-
-
setsid();
-
-
sa.sa_handler = SIG_IGN;
-
sigemptyset(&sa.sa_mask);
-
sa.sa_flags = 0;
-
if (sigaction(SIGHUP, &sa, NULL) < 0) {
-
printf("sigaction failed");
-
exit(1);
-
}
-
if ((pid = fork()) < 0) {
-
printf("second fork failed");
-
exit(1);
-
} else if (pid > 0) {
-
exit(0);
-
}
-
-
if (chdir("/") < 0) {
-
printf("chdir failed");
-
exit(1);
-
}
-
-
if (rl.rlim_max == RLIM_INFINITY)
-
rl.rlim_max = 1024;
-
for (i = 0; i < rl.rlim_max; i )
-
close(i);
-
-
fd0 = open("/dev/null", O_RDWR);
-
fd1 = dup(0);
-
fd2 = dup(0);
-
-
openlog(cmd, LOG_CONS, LOG_DAEMON);
-
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
-
syslog(LOG_ERR, "unexpected file descriptor %d %d %d", fd0, fd1, fd2);
-
exit(1);
-
}
-
}
首先说说什么是服务,端口号大家都知道,
比如smtp这个就是服务,而它使用的端口号是25,但是系统怎么知道它需要使用端口25呢,就是通过在/etc/services这个文件里进行登记,
打开它你会发现里面登记了几乎所有的像ftp,dns这样的公开的服务,同样的道理,如果我们要使用自己的服务,也需要在里面登记,但要注意端口号不能小
于1024,并且不能和已登记的重复,还有一点是关于服务名,这个名字没有必要和程序名一样,比如上面的代码,
程序名是ruptimed,而服务名是ruptime,
只要你认为能代表程序的功能就行。现在大家应该知道了什么是服务了,就是程序的代号。现在再回到getaddrinfo这个函数,当我将第二个参数设为服
务名(ruptime)时,返回的结果里的端口号就是在/etc/services里登记的端口号,有人会说了,那第一个参数呢?是这样的,大家可以打开
/etc/hosts这个文件,在开头有几行主机名和地址的列表,下面是我的hosts文件的内容:
代码:
127.0.0.1 localhost
192.168.1.104 wawxdyy
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
如果我将第一个参数设为localhost,那么返回的地址就是127.0.0.1,如果设为wawxdyy,返回的就是192.168.1.104。
如果要想以服务的形式运行服务器程序,这两个参数一定要明确指定,并且主机名不能是localhost,只有这样返回的结果才是有效的,才能用于后面的绑定。
2
服务器程序用端口号的形式发布:
这种情况下就没有必要在/etc/services里登记了,但是hint结构的ai_flags的参数设定有注意的地方,在上面的那种情况,因为主机名
和服务名都明确提供了,所以即使ai_flags设为0也能返回正确的结果,但是现在我们将第二个参数设为端口号,这样的话,我们必须将hint结构的
ai_flags设为AI_PASSIVE,这样返回的结果才能用于后面的监听绑定,否则不能,
因为AI_PASSIVE就是告诉getaddrinfo返回的地址是用于监听绑定的。下面是相关的代码:
代码:
- #include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
#include <errno.h>
-
#include <unistd.h>
-
#include <syslog.h>
-
#include <netdb.h>
-
#include <sys/socket.h>
-
#include <netinet/in.h>
-
#include <arpa/inet.h>
-
#include <sys/wait.h>
-
#include <fcntl.h>
-
-
#ifndef HOST_NAME_MAX
-
#define HOST_NAME_MAX 64
-
#endif
-
-
#define BUFLEN 128
-
#define QLEN 10
-
-
extern int initserver(int, const struct sockaddr *, socklen_t, int);
-
extern void daemonize(const char *);
-
-
void
-
serve(int sockfd)
-
{
-
int clfd, status;
-
pid_t pid;
-
-
for (;;) {
-
if ((clfd = accept(sockfd, NULL, NULL)) < 0) {
-
syslog(LOG_ERR, "accept error: %m\n");
-
exit(-1);
-
}
-
-
if ((pid = fork()) < 0) {
-
syslog(LOG_ERR, "fork error: %m\n");
-
exit(-1);
-
} else if (pid == 0) {
-
if (dup2(clfd, STDOUT_FILENO) != STDOUT_FILENO ||
-
dup2(clfd, STDERR_FILENO) != STDERR_FILENO) {
-
syslog(LOG_ERR, "dup2 error: %m\n");
-
exit(-1);
-
}
-
close(clfd);
-
execl("/usr/bin/uptime", "uptime", (char *)0);
-
syslog(LOG_ERR, "unexpected return from execl: %m");
-
} else {
-
close(clfd);
-
waitpid(pid, &status, 0);
-
}
-
}
-
}
-
-
int
-
main(void)
-
{
-
struct addrinfo *ailist, *aip;
-
struct addrinfo hint;
-
struct sockaddr_in *sinp;
-
int sockfd;
-
int err, n;
-
char *host;
-
char buf[INET_ADDRSTRLEN];
-
-
#ifdef _SC_HOST_NAME_MAX
-
n = sysconf(_SC_HOST_NAME_MAX);
-
if (n < 0)
-
#endif
-
n = HOST_NAME_MAX;
-
-
if ((host = malloc(n)) == NULL) {
-
printf("malloc error: %s\n", strerror(errno));
-
exit(-1);
-
}
-
if (gethostname(host, n) < 0) {
-
printf("gethostname error: %s\n", strerror(errno));
-
exit(-1);
-
}
-
syslog(LOG_ERR, "hostname is %s", host);
-
daemonize("ruptimed");
-
-
hint.ai_flags = AI_PASSIVE;
-
hint.ai_family = 0;
-
hint.ai_socktype = SOCK_STREAM;
-
hint.ai_protocol = 0;
-
hint.ai_addrlen = 0;
-
hint.ai_addr = NULL;
-
hint.ai_canonname = NULL;
-
hint.ai_next = NULL;
-
if ((err = getaddrinfo(NULL, "2000", &hint, &ailist)) != 0) {
-
syslog(LOG_ERR, "getaddrinfo error: %s", gai_strerror(err));
-
exit(-1);
-
}
-
for (aip = ailist; aip != NULL; aip = aip->ai_next) {
-
sinp = (struct sockaddr_in *)aip->ai_addr;
-
short port = ntohs(sinp->sin_port);
-
syslog(LOG_ERR, "port is %d\n", port);
-
if (inet_ntop(aip->ai_family, &sinp->sin_addr, buf, INET_ADDRSTRLEN) != NULL)
-
注:暂存的内容只能恢复到当前文章的编辑器中,如需恢复到其他文章中,请编辑该文章并从暂存箱中恢复;或者直接复制以上内容,手工恢复到相关文章。
-
恢复到编辑器
关闭
阅读(460) | 评论(0) | 转发(0) |