您的位置:首页 > 其它

getaddrinfo函数详解

2014-09-20 10:35 309 查看
http://blog.csdn.net/andyxie407/article/details/1672325 关于怎样实现守护进程的。

函数原型:

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地址的字符串,比如“ 192.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)

注:暂存的内容只能恢复到当前文章的编辑器中,如需恢复到其他文章中,请编辑该文章并从暂存箱中恢复;或者直接复制以上内容,手工恢复到相关文章。

恢复到编辑器 关闭
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: