Chinaunix首页 | 论坛 | 博客
  • 博客访问: 316202
  • 博文数量: 90
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 113
  • 用 户 组: 普通用户
  • 注册时间: 2016-01-27 19:56
文章分类

全部博文(90)

文章存档

2017年(12)

2016年(78)

分类: LINUX

2016-04-14 10:02:25

原文地址:getaddrinfo函数详解 作者:kjqin

函数原型:

 

  1. int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
  2.  void freeaddrinfo(struct addrinfo *res);
  3.  
  4. struct addrinfo
  5. {        int ai_flags;
  6.          int ai_family;
  7.          int ai_socktype;
  8.          int ai_protocol;
  9.          size_t ai_addrlen;
  10.          struct sockaddr *ai_addr; /* 我觉得这个成员是这个函数最大的便利。 */
  11.          char *ai_canonname;
  12.          struct addrinfo *ai_next;
  13. };
  14. 参数: 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 参数。 为什么要有这个参数 ? 

看一个小程序:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdarg.h>
  4. #include <string.h>
  5. #include <fcntl.h>
  6. #include <time.h>
  7. #include <ctype.h>
  8. #include <unistd.h>
  9. #include <errno.h>
  10. #include <sys/wait.h>
  11. #include <signal.h>
  12. #include <sys/types.h>
  13. #include <netinet/in.h>
  14. #include <arpa/inet.h>
  15. #include <sys/stat.h>
  16. #include <netdb.h>
  17. #include <sys/socket.h>
  18. void main()
  19. {
  20. struct addrinfo *ailist, *aip;
  21. struct addrinfo hint;
  22. struct sockaddr_in *sinp;
  23.  char *hostname = "luoyedeshan"; /* 这是我的用户名 */
  24. char buf[INET_ADDRSTRLEN];
  25. char *server = "3294"; /* 这是服务端口号 */
  26. const char *addr;
  27. int ilRc;
  28. hint.ai_family = AF_UNSPEC; /* hint 的限定设置 */
  29. hint.ai_socktype = 0;        /* 这里可是设置 socket type . 比如 SOCK——DGRAM */
  30. hint.ai_flags = AI_PASSIVE; /* flags 的标志很多 。常用的有AI_CANONNAME; */
  31. hint.ai_protocol = 0; /* 设置协议 一般为0,默认 */
  32. hint.ai_addrlen = 0; /* 下面不可以设置,为0,或者为NULL */
  33. hint.ai_canonname = NULL;
  34. hint.ai_addr = NULL;
  35. hint.ai_next = NULL;
  36. ilRc = getaddrinfo(hostname, server, &hint, &ailist);
  37. if (ilRc < 0)
  38. {
  39.  char str_error[100];
  40. strcpy(str_error,gai_strerror(errno));
  41. printf("str_error = %s", str_error);
  42. return;
  43. }
  44.  
  45. for (aip = ailist; aip != NULL; aip = aip->ai_next) /* 显示获取的信息 */
  46. {
  47. sinp = (struct sockaddr_in *)aip->ai_addr; /* 为什么是for 循环 ,先向下看 */
  48. addr = inet_ntop(AF_INET, &sinp->sin_addr, buf, INET_ADDRSTRLEN);
  49. printf(" addr = %s", addr?addr:"unknow ");
  50. printf("port = %d ", ntohs(sinp->sin_port));
  51. printf(" \n");
  52. }
  53. }

/*             执行结果如下                   */

[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,服务器端以服务的形式提供给用户,下面的代码是用到的库函数
代码:

  1. #include <sys/socket.h>
  2. #include <sys/resource.h>
  3. #include <sys/stat.h>
  4. #include <errno.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <signal.h>
  10. #include <fcntl.h>
  11. #include <syslog.h>

  12. #define MAXSLEEP 128

  13. int
  14. connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
  15. {
  16. int nsec;

  17. for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) {
  18. if (connect(sockfd, addr, alen) == 0)
  19. return(0);
  20. if (nsec <= MAXSLEEP/2)
  21. sleep(nsec);
  22. }
  23. return(-1);
  24. }

  25. int
  26. initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
  27. {
  28. int fd;
  29. int err = 0;
  30. int reuse = 1;

  31. if ((fd = socket(addr->sa_family, type, 0)) < 0)
  32.     return(-1);
  33. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0) {
  34. err = errno;
  35. goto errout;
  36. }
  37. if (bind(fd, addr, alen) < 0) {
  38. err = errno;
  39. goto errout;
  40. }
  41. if ((type == SOCK_STREAM) || (type == SOCK_SEQPACKET)) {
  42. if (listen(fd, qlen) < 0) {
  43. err = errno;
  44. goto errout;
  45. }
  46. }
  47. return(fd);
  48. errout:
  49. close(fd);
  50. errno = err;
  51. return(-1);
  52. }

  53. void
  54. daemonize(const char *cmd)
  55. {
  56. int i, fd0, fd1, fd2;
  57. pid_t pid;
  58. struct rlimit rl;
  59. struct sigaction sa;

  60. umask(0);

  61. if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
  62. printf("getrlimit failed");
  63. exit(1);
  64. }

  65. if ((pid = fork()) < 0) {
  66. printf("first fork failed");
  67. exit(1);
  68. } else if (pid > 0) {
  69. exit(0);
  70. }

  71. setsid();

  72. sa.sa_handler = SIG_IGN;
  73. sigemptyset(&sa.sa_mask);
  74. sa.sa_flags = 0;
  75. if (sigaction(SIGHUP, &sa, NULL) < 0) {
  76. printf("sigaction failed");
  77. exit(1);
  78. }
  79. if ((pid = fork()) < 0) {
  80. printf("second fork failed");
  81. exit(1);
  82. } else if (pid > 0) {
  83. exit(0);
  84. }

  85. if (chdir("/") < 0) {
  86. printf("chdir failed");
  87. exit(1);
  88. }

  89. if (rl.rlim_max == RLIM_INFINITY)
  90. rl.rlim_max = 1024;
  91. for (i = 0; i < rl.rlim_max; i )
  92. close(i);

  93. fd0 = open("/dev/null", O_RDWR);
  94. fd1 = dup(0);
  95. fd2 = dup(0);

  96. openlog(cmd, LOG_CONS, LOG_DAEMON);
  97. if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
  98. syslog(LOG_ERR, "unexpected file descriptor %d %d %d", fd0, fd1, fd2);
  99. exit(1);
  100. }
  101. }
首先说说什么是服务,端口号大家都知道, 比如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返回的地址是用于监听绑定的。下面是相关的代码:
代码:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <unistd.h>
  6. #include <syslog.h>
  7. #include <netdb.h>
  8. #include <sys/socket.h>
  9. #include <netinet/in.h>
  10. #include <arpa/inet.h>
  11. #include <sys/wait.h>
  12. #include <fcntl.h>

  13. #ifndef HOST_NAME_MAX
  14. #define HOST_NAME_MAX 64
  15. #endif

  16. #define BUFLEN 128
  17. #define QLEN 10

  18. extern int initserver(int, const struct sockaddr *, socklen_t, int);
  19. extern void daemonize(const char *);

  20. void
  21. serve(int sockfd)
  22. {
  23. int clfd, status;
  24. pid_t pid;

  25. for (;;) {
  26. if ((clfd = accept(sockfd, NULL, NULL)) < 0) {
  27. syslog(LOG_ERR, "accept error: %m\n");
  28. exit(-1);
  29. }

  30. if ((pid = fork()) < 0) {
  31. syslog(LOG_ERR, "fork error: %m\n");
  32. exit(-1);
  33. } else if (pid == 0) {
  34. if (dup2(clfd, STDOUT_FILENO) != STDOUT_FILENO ||
  35.     dup2(clfd, STDERR_FILENO) != STDERR_FILENO) {
  36. syslog(LOG_ERR, "dup2 error: %m\n");
  37. exit(-1);
  38. }
  39. close(clfd);
  40. execl("/usr/bin/uptime", "uptime", (char *)0);
  41. syslog(LOG_ERR, "unexpected return from execl: %m");
  42. } else {
  43. close(clfd);
  44. waitpid(pid, &status, 0);
  45. }
  46. }
  47. }

  48. int
  49. main(void)
  50. {
  51. struct addrinfo *ailist, *aip;
  52. struct addrinfo hint;
  53. struct sockaddr_in *sinp;
  54. int sockfd;
  55. int err, n;
  56. char *host;
  57. char buf[INET_ADDRSTRLEN];

  58. #ifdef _SC_HOST_NAME_MAX
  59. n = sysconf(_SC_HOST_NAME_MAX);
  60. if (n < 0)
  61. #endif
  62. n = HOST_NAME_MAX;

  63. if ((host = malloc(n)) == NULL) {
  64. printf("malloc error: %s\n", strerror(errno));
  65. exit(-1);
  66. }
  67. if (gethostname(host, n) < 0) {
  68. printf("gethostname error: %s\n", strerror(errno));
  69. exit(-1);
  70. }
  71. syslog(LOG_ERR, "hostname is %s", host);
  72. daemonize("ruptimed");

  73. hint.ai_flags = AI_PASSIVE;
  74. hint.ai_family = 0;
  75. hint.ai_socktype = SOCK_STREAM;
  76. hint.ai_protocol = 0;
  77. hint.ai_addrlen = 0;
  78. hint.ai_addr = NULL;
  79. hint.ai_canonname = NULL;
  80. hint.ai_next = NULL;
  81. if ((err = getaddrinfo(NULL, "2000", &hint, &ailist)) != 0) {
  82. syslog(LOG_ERR, "getaddrinfo error: %s", gai_strerror(err));
  83. exit(-1);
  84. }
  85. for (aip = ailist; aip != NULL; aip = aip->ai_next) {
  86. sinp = (struct sockaddr_in *)aip->ai_addr;
  87. short port = ntohs(sinp->sin_port);
  88. syslog(LOG_ERR, "port is %d\n", port);
  89. if (inet_ntop(aip->ai_family, &sinp->sin_addr, buf, INET_ADDRSTRLEN) != NULL)
  90. 注:暂存的内容只能恢复到当前文章的编辑器中,如需恢复到其他文章中,请编辑该文章并从暂存箱中恢复;或者直接复制以上内容,手工恢复到相关文章。
  91. 恢复到编辑器   关闭
阅读(1786) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~