Unix/Linux编程:getaddrinfo、getnameinfo

getaddrinfo

getaddrinfogethostbyname、gethostbyaddr的升级版

  • getaddrinfo()函数将主机和服务名转换成 IP 地址和端口号,它作为过时的 gethostbyname()和 getservbyname()函数的(可重入的)接替者被定义在了 POSIX.1g 中。它是协议无关的,既可用于IPv4也可用于IPv6。

  • 给定一个主机名和服务器名,getaddrinfo()函数返回一个 socket 地址结构列表,每个结构都包含一个地址和端口号

#include<netdb.h>

// 成功时返回 0,发生错误时返回非零值
int getaddrinfo (const char *__restrict nodename,
			const char *__restrict service,
			const struct addrinfo *__restrict hints,
			struct addrinfo **__restrict result )

const char *gai_strerror (int __ecode) 
void freeaddrinfo (struct addrinfo *__ai)

getaddrinfo()以 nodename、service 以及 hints 参数作为输入,其中 nodename参数包含一个主机名或一个以IPv4点分十进制标记或IPv6十六进制字符串标记的数值地址字符串。service 参数包含一个服务名或一个十进制端口号。hints 参数指向一个 addrinfo 结构,该结构规定了选择通过 result 返回的 socket 地址结构的标准

getaddrinfo()会动态地分配一个包含 addrinfo 结构的链表并将 result 指向这个列表的表头。每个 addrinfo 结构包含一个指向与 host 和 service 对应的 socket 地址结构的指针。addrinfo 结构的形式如下:

struct addrinfo
{
  int ai_flags;			/* Input flags.  */
  int ai_family;		/* Protocol family for socket.  */
  int ai_socktype;		/* Socket type.  */
  int ai_protocol;		/* Protocol for socket.  */
  socklen_t ai_addrlen;		/* Length of socket address.  */
  struct sockaddr *ai_addr;	/* Socket address for socket.  */
  char *ai_canonname;		/* Canonical name for service location.  */
  struct addrinfo *ai_next;	/* 指向链表的下一个结点 */
};

result 参数返回一个结构列表而不是单个结构,因为与在 nodename、service 以及 hints 中指定的标准对应的主机和服务组合可能有多个。如查询拥有多个网络接口的主机时可能会返回多个地址结构。此外,如果将 hints.ai_socktype 指定为 0,那么就可能会返回两个结构—一个用于 SOCK_DGRAM socket,另一个用于 SOCK_STREAM socket—前提是给定的 service 同时对 TCP 和 UDP 可用

通过 result 返回的 addrinfo 结构的字段描述了关联 socket 地址结构的属性:
(1)ai_flags 用来指定如何处理地址和名字,可取得值如下:
在这里插入图片描述

(2)ai_family 表示该 socket 地址结构的类型
在这里插入图片描述

(3)ai_socktype socket的类型,一般被设置成 SOCK_STREAM 或 SOCK_DGRAM,表示这个地址结构是用于 TCP 服务还是用于 UDP服务。
在这里插入图片描述

  • ai_protocol 字段会返回与地址族和 socket 类型匹配的协议值。(ai_family、ai_socktype以及 ai_protocol 三个字段为调用 socket()创建该地址上的 socket 时所需的参数提供了取值。)
  • ai_addrlen 字段给出了 ai_addr 指向的 socket 地址结构的大小(字节数)
  • ai_canonname字段仅由第一个 addrinfo 结构使用并且其前提
  • ai_addr 字段指向 socket地址结构(IPv4 时是一个 in_addr 结构,IPv6 时是一个 in6_addr 结构)。在 hints.ai_flags 中使用了 AI_CANONNAME 字段。

可导致返回多个addrinfo结构的情形有两个:

  • 如果与nodename参数关联的地址有多个,那么适用于所请求地址族(hints的ai_family设置)的每个地址都返回一个对应的结构
  • 如果sevice参数指定的服务支持多个套机字类型,那么每个套接字类型都可能返回一个对应的结构

在这里插入图片描述
与gethostbyname()一样,getaddrinfo()可能需要向一台DNS 服务器发送一个请求,并且这个请求可能需要花费一段时间来完成。同样的过程也适用于getnameinfo(),

1、参数:

  1. nodename
  • 主机名(“www.baidu.com”)或者是数字化的地址字符串(IPv4的点分十进制串(“192.168.1.100”)或者IPv6的16进制串(“2000::1:2345:6789:abcd”)),
  • 如果 ai_flags中设置了AI_NUMERICHOST标志,那么该参数只能是数字化的地址字符串,不能是域名,该标志的作用就是阻止进行域名解析。
  • nodename 和 servname 可以设置为NULL,但是同时只能有一个为NUL。
  1. servname
  • 服务名可以是十进制的端口号(“8080”)字符串,也可以是已定义的服务名称,如"ftp"、"http"等,详细请查看/etc/services 文件,最后翻译成对应服务的端口号。
  • 如果此参数设置为NULL,那么返回的socket地址中的端口号不会被设置。
  • 如果 ai_flags 设置了AI_NUMERICSERV 标志并且该参数未设置为NULL,那么该参数必须是一个指向10进制的端口号字符串,不能设定成服务名,该标志就是用来阻止服务名解析。
  1. hints

hints 参数为如何选择 getaddrinfo()返回的 socket 地址结构指定了更多的标准。当用作 hints参数时只能设置 addrinfo 结构的 ai_flags、ai_family、ai_socktype 以及 ai_protocol 字段,其他字段未用到,并将应该根据具体情况将其初始化为 0 或 NULL

  • 该参数指向用户设定的struct addrinfo 结构体,只能设定该结构体中ai_family、ai_socktype、ai_protocolai_flags四个域,其他域必须设置为0 或者 NULL, 通常是申请 结构体变量后使用memset()初始化再设定指定的四个域。
  • 该参数可以设置为NULL,等价于 ai_socktype = 0, ai_protocol = 0,ai_family = AF_UNSPEC, ai_flags = 0 (在GNU Linux中ai_flag = AI_V4MAPPED | AI_ADDRCONFIG,可以看作是GNU的改进)。
    • ai_family :  字段选择了返回的 socket 地址结构的域,取值范围:AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNSPEC(IPv4 and IPv6)
      • 如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;
      • 如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。
      • AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。
      • 如果某个主机既有AAAA记录(IPV6)地址,同时又有A记录(IPV4)地址,那么AAAA记录将作为sockaddr_in6结构返回,而A记录则作为sockaddr_in结构返回。
    • ai_socktype:
      • 具体类型请查看struct addrinfo 中的enum __socket_type类型,用于设定使用返回的 socket 地址结构的 socket 类型,常用的有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW, 设置为0表示所有类型都可以。
      • 如果将这个字段指定为 SOCK_DGRAM,那么查询将会在 UDP 服务上执行,对应的 socket 地址结构会通过result 返回。如果指定了 SOCK_STREAM,那么将会执行一个 TCP 服务查询。如果将hints.ai_socktype 指定为 0,那么任意类型的 socket 都是可接受的。
    • ai_protocol: 段为返回的地址结构选择了 socket 协议。具体取值范围请查看 Ip Protocol ,常用的有 IPPROTO_TCP、IPPROTO_UDP 等,设置为0表示调用者接受任何协议。
    • ai_flags:附加选项,字段是一个位掩码,它会改变 getaddrinfo()的行为。这个字段的取值是下列值中的零个或多个取 OR 得来的,具体取值范围请查看struct addrinfo , 常用的标志如下:
      • AI_PASSIVE: 套接字将用于被动打开
        (1)如果设置了 AI_PASSIVE 标志,并且 nodename 是 NULL, 那么返回的socket地址可以用于的bind()函数,返回的地址是通配符地址(wildcard address, IPv4时是INADDR_ANY,IPv6时是IN6ADDR_ANY_INIT),这样应用程序(典型是server)就可以使用这个通配符地址用来接收任何请求主机地址的连接。
        (2)如果设置了 AI_PASSIVE 标志,并且 nodename 不是NULL ,那么 AI_PASSIVE 标志被忽略;
        (3)如果未设置AI_PASSIVE标志,返回的socket地址可以用于connect(), sendto(), 或者 sendmsg()函数。
        (4)如果 nodename 是NULL,那么网络地址会被设置为lookback接口地址(IPv4时是INADDR_LOOPBACK,IPv6时是IN6ADDR_LOOPBACK_INIT),这种情况下,应用是想与运行在同一个主机上另一个应用通信。
      • AI_CANONNAME:返回主机的规范名字
        (1)请求canonical(主机的official name)名字。如果设置了该标志,那么 res 返回的第一个 struct addrinfo 中的 ai_canonname 域会存储official name的指针。
      • AI_NUMERICHOST
        (1)阻止域名解析,nostname必须是一个地址串。具体见 nodename 中的说明。
      • AI_NUMERICSERV
        (1)阻止服务名解析,servoce必须是一个十进制端口号。具体见 servname 中的说明
      • AI_V4MAPPED:如果同时指定ai_family为AF_INT6时,如果没有可用的AAAA记录,就i返回A记录对应的IPv4映射的IPV6地址
        (1)当 ai_family 指定为AF_INT6(IPv6)时,如果没有找到IPv6地址,那么会返回IPv4-mapped IPv6 地址,也就是说如果没有找到AAAA record(用来将域名解析到IPv6地址的DNS记录),那么就查询A record(IPv4),将找到的IPv4地址映射到IPv6地址, IPv4-mapped IPv6 地址其实是IPv6内嵌IPv4的一种方式,地址的形式为"0::FFFF:a.b.c.d",例如"::ffff:192.168.89.9"(混合格式)这个地址仍然是一个IPv6地址,只是"0000:0000:0000:0000:0000:ffff:c0a8:5909"(16机制格式)的另外一种写法罢了。
        (2)当 ai_family 不是AF_INT6(IPv6)时,该标志被忽略。
      • AI_ALL:
        (1)查询IPv4和IPv6地址
      • AI_ADDRCONFIG:
        (1)只有当主机配置了IPv4地址才进行查询IPv4地址;只有当主机配置了IPv6地址才进行查询IPv6地址.
  1. result
  • 该参数获取一个指向存储结果的 struct addrinfo 结构体列表(由其中的ai_next成员串接起来),使用完成后调用 freeaddrinfo() 释放存储结果空间。

在这里插入图片描述

如果 getaddrinfo() 函数执行成功,返回值为 0 , 其他情况返回值表示错误种别。使用函数gai_strerror() 可以获取可读性的错误信息,用法用strerror()相同,

释放 addrinfo 列表:freeaddrinfo()

getaddrinfo()函数会动态地为 result 引用的所有结构分配内存(图 59-3),其结果是调用者必须要在不再需要这些结构时释放它们。使用 freeaddrinfo()函数可以方便地在一个步骤中执行这个释放任务。

   void freeaddrinfo(struct addrinfo *res);

如果希望保留 addrinfo 结构或其关联的 socket 地址结构的一个副本,那么必须要在调用freeaddrinfo()之前复制这些结构。

getnameinfo()函数

getnameinfo()函数是 getaddrinfo()的逆函数。给定一个 socket 地址结构(IPv4 或 IPv6),它会返回一个包含对应的主机和服务名的字符串或者在无法解析名字时返回一个等价的数值

NAME
       getnameinfo  - address-to-name translation in protocol-independent man‐
       ner

SYNOPSIS
       #include <sys/socket.h>
       #include <netdb.h>

       int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                       char *host, size_t hostlen,
                       char *serv, size_t servlen, int flags);

sa参数是一个指向待转换的 socket 地址结构的指针,该结构的长度是由 salen指定的。通常,sa和 salen的值是从 accept()、recvfrom()、getsockname()或getpeername()调用中获得的。

得到的主机和服务名是以 null 结尾的字符串,它们会被存储在 host 和 serv指向的缓冲器中。调用者必须要为这些缓冲器分配空间并将它们的大小传入 hostlen 和 servlen。

<netdb.h>头文件定义了两个常量来辅助计算这些缓冲器的大小。NI_MAXHOST 指出了返回的主机名字符串的最大字节数,其取值为 1025。NI_MAXSERV 指出了返回的服务名字符串的最大字节数,其取值为 32。这两个常量没有在 SUSv3中得到规定,但所有提供 getnameinfo()的 UNIX 实现都对它们进行了定义。(从 glibc 2.8 起,必须要定义_BSD_SOURCE、_SVID_SOURCE 或_GNU_SOURCE 中的其中一个特性文本宏才能获取 NI_MAXHOST 和NI_MAXSERV 的定义。

如果不想获取主机名,那么可以将 host 指定为 NULL 并且将 hostlen 指定为 0。同样地,如果不需要服务名,那么可以将 serv 指定为 NULL 并且将 servlen 指定为 0。但是 host 和serv 中至少有一个必须为非 NULL 值(并且对应的长度参数必须为非零)

最后一个参数 flags 是一个位掩码,它控制着 getnameinfo()的行为,其取值为下面这些常量取 OR

  • NI_DGRAM :在默认情况下,getnameinfo()返回与流 socket(即 TCP)服务对应的名字。通常,这是无关紧要的,因为与 TCP 和 UDP 端口对应的服务名通常是相同的,但在一些名字不同的场景中,NI_DGRAM 标记会强制返回数据报 socket(即 UDP)服务的名字。
  • NI_NAMEREQD :在默认情况下,如果无法解析主机名,那么在 host 中会返回一个数值地址字符串。如果指定了 NI_NAMEREQD,那么就会返回一个错误(EAI_NONAME)
  • NI_NOFQDN :在默认情况下会返回主机的完全限定域名。指定 NI_NOFQDN 标记会导致当主机位于局域网中时只返回名字的第一部分(即主机名)
  • NI_NUMERICHOST :强制在 host 中返回一个数值地址字符串。这个标记在需要避免可能耗时较长的 DNS 服务器调用时是比较有用的。
  • NI_NUMERICSERV :强制在 service 中返回一个十进制端口号字符串。这个标记在知道端口号不对应于服务名
    时—如它是一个由内核分配给 socket 的临时端口号—以及需要避免不必要的搜索/etc/services 的低效性时是比较有用的。

getnameinfo()在成功时会返回0,发生错误时会返回一个非零错误码

错误诊断:gai_strerror()

getaddrinfo()在发生错误时会返回下表中给出的一个非零错误码
在这里插入图片描述
gai_strerror()函数会返回一个描述该错误的字符串。

 const char *gai_strerror(int errcode);

实践

使用

1、man getaddrinfo 最后有客户端和服务器端2个例子。

2、只取第一个IP地址

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>//结构体addrinfo, in_addr
#include <netinet/in.h>
#include <arpa/inet.h>



int main(){
    char* hostname = "www.cnblogs.com";//博客园的网址,返回实际IP地址
    struct addrinfo hints, *res;
    struct in_addr addr;
    int err;

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET;

    if((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0){
        printf("error %d : %s\n", err, gai_strerror(err));
        return 1;
    }
    addr.s_addr = ((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr;
    printf("ip addresss: %s\n", inet_ntoa(addr));//博客园的网址,返回实际IP地址  ip addresss: 114.55.187.58

    freeaddrinfo(res);

    return 0;
}

2、显示所有域名查找到的所有IP地址

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>//结构体addrinfo, in_addr
#include <netinet/in.h>
#include <arpa/inet.h>



int main(int argc, char **argv){
    char *hostname;
    struct addrinfo hints,*result, *rp;
    int err;
    struct in_addr addr;

    //没有输入域名时,博客园的网址,返回实际IP地址
    hostname = argc < 2 ? (char *)"www.cnblogs.com" : argv[1];

    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_INET;


    if((err = getaddrinfo(hostname, NULL, &hints, &result)) != 0){
        printf("error %d : %s\n", err, gai_strerror(err));
        return 1;
    }


    for (rp = result; rp != NULL; rp = rp->ai_next) {
        addr.s_addr = ((struct sockaddr_in*)(rp->ai_addr))->sin_addr.s_addr;
        printf("ip addresss: %s\n", inet_ntoa(addr));
    }

    freeaddrinfo(result);

    return 0;
}

在这里插入图片描述
3、服务端

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>

#define BUF_SIZE 500

int main(int argc, char *argv[])
{
   struct addrinfo hints;
   struct addrinfo *result, *rp;
   int sfd, s;
   struct sockaddr_storage peer_addr;
   socklen_t peer_addr_len;
   ssize_t nread;
   char buf[BUF_SIZE];

   if (argc != 2) {
       fprintf(stderr, "Usage: %s port\n", argv[0]);
       exit(EXIT_FAILURE);
   }

   memset(&hints, 0, sizeof(struct addrinfo));
   hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
   hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
   hints.ai_flags = AI_PASSIVE;    /* For wildcard IP address */
   hints.ai_protocol = 0;          /* Any protocol */
   hints.ai_canonname = NULL;
   hints.ai_addr = NULL;
   hints.ai_next = NULL;

   s = getaddrinfo(NULL, argv[1], &hints, &result);
   if (s != 0) {
       fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
       exit(EXIT_FAILURE);
   }

   /* getaddrinfo() returns a list of address structures.
      Try each address until we successfully bind(2).
      If socket(2) (or bind(2)) fails, we (close the socket
      and) try the next address. */

   for (rp = result; rp != NULL; rp = rp->ai_next) {
       sfd = socket(rp->ai_family, rp->ai_socktype,
               rp->ai_protocol);
       if (sfd == -1)
           continue;

       if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
           break;                  /* Success */

       close(sfd);
   }

   if (rp == NULL) {               /* No address succeeded */
       fprintf(stderr, "Could not bind\n");
       exit(EXIT_FAILURE);
   }

   freeaddrinfo(result);           /* No longer needed */

   /* Read datagrams and echo them back to sender */
   for (;;) {
       peer_addr_len = sizeof(struct sockaddr_storage);
       nread = recvfrom(sfd, buf, BUF_SIZE, 0,
               (struct sockaddr *) &peer_addr, &peer_addr_len);
       if (nread == -1)
           continue;               /* Ignore failed request */

       char host[NI_MAXHOST], service[NI_MAXSERV];

       s = getnameinfo((struct sockaddr *) &peer_addr,
                       peer_addr_len, host, NI_MAXHOST,
                       service, NI_MAXSERV, NI_NUMERICSERV);
       if (s == 0)
           printf("Received %zd bytes from %s:%s\n",
                   nread, host, service);
       else
           fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));

       if (sendto(sfd, buf, nread, 0,
                   (struct sockaddr *) &peer_addr,
                   peer_addr_len) != nread)
           fprintf(stderr, "Error sending response\n");
   }
}

客户端

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define BUF_SIZE 500

int
main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int sfd, s, j;
    size_t len;
    ssize_t nread;
    char buf[BUF_SIZE];

    if (argc < 3) {
        fprintf(stderr, "Usage: %s host port msg...\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Obtain address(es) matching host/port */

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = 0;
    hints.ai_protocol = 0;          /* Any protocol */

    s = getaddrinfo(argv[1], argv[2], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully connect(2).
       If socket(2) (or connect(2)) fails, we (close the socket
       and) try the next address. */

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype,
                     rp->ai_protocol);
        if (sfd == -1)
            continue;

        if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
            break;                  /* Success */

        close(sfd);
    }

    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not connect\n");
        exit(EXIT_FAILURE);
    }

    freeaddrinfo(result);           /* No longer needed */

    /* Send remaining command-line arguments as separate
       datagrams, and read responses from server */

    for (j = 3; j < argc; j++) {
        len = strlen(argv[j]) + 1;
                /* +1 for terminating null byte */

        if (len > BUF_SIZE) {
            fprintf(stderr,
                    "Ignoring long message in argument %d\n", j);
            continue;
        }

        if (write(sfd, argv[j], len) != len) {
            fprintf(stderr, "partial/failed write\n");
            exit(EXIT_FAILURE);
        }

        nread = read(sfd, buf, BUF_SIZE);
        if (nread == -1) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        printf("Received %zd bytes: %s\n", nread, buf);
    }

    exit(EXIT_SUCCESS);
}

#include<stdio.h>
#include<stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>

void
print_family(struct addrinfo *aip)
{
    printf(" family ");
    switch (aip->ai_family) {
    case AF_INET:
        printf("inet");
        break;
    case AF_INET6:
        printf("inet6");
        break;
    case AF_UNIX:
        printf("unix");
        break;
    case AF_UNSPEC:
        printf("unspecified");
        break;
    default:
        printf("unknown");
    }
}

void
print_type(struct addrinfo *aip)
{
    printf(" type ");
    switch (aip->ai_socktype) {
    case SOCK_STREAM:
        printf("stream");
        break;
    case SOCK_DGRAM:
        printf("datagram");
        break;
    case SOCK_SEQPACKET:
        printf("seqpacket");
        break;
    case SOCK_RAW:
        printf("raw");
        break;
    default:
        printf("unknown (%d)", aip->ai_socktype);
    }
}

void
print_protocol(struct addrinfo *aip)
{
    printf(" protocol ");
    switch (aip->ai_protocol) {
    case 0:
        printf("default");
        break;
    case IPPROTO_TCP:
        printf("TCP");
        break;
    case IPPROTO_UDP:
        printf("UDP");
        break;
    case IPPROTO_RAW:
        printf("raw");
        break;
    default:
        printf("unknown (%d)", aip->ai_protocol);
    }
}

void
print_flags(struct addrinfo *aip)
{
    printf("flags");
    if (aip->ai_flags == 0) {
        printf(" 0");
    } else {
        if (aip->ai_flags & AI_PASSIVE)
            printf(" passive");
        if (aip->ai_flags & AI_CANONNAME)
            printf(" canon");
        if (aip->ai_flags & AI_NUMERICHOST)
            printf(" numhost");
#if defined(AI_NUMERICSERV)
        if (aip->ai_flags & AI_NUMERICSERV)
            printf(" numserv");
#endif
#if defined(AI_V4MAPPED)
        if (aip->ai_flags & AI_V4MAPPED)
            printf(" v4mapped");
#endif
#if defined(AI_ALL)
        if (aip->ai_flags & AI_ALL)
            printf(" all");
#endif
    }
}

int
main(int argc, char *argv[])
{
    struct addrinfo        *ailist, *aip;
    struct addrinfo        hint;
    struct sockaddr_in    *sinp;
    const char             *addr;
    int                 err;
    char                 abuf[INET_ADDRSTRLEN];

    if (argc != 3)
        printf("usage: %s nodename service", argv[0]);
    hint.ai_flags = AI_CANONNAME;
    hint.ai_family = 0;
    hint.ai_socktype = 0;
    hint.ai_protocol = 0;
    hint.ai_addrlen = 0;
    hint.ai_canonname = NULL;
    hint.ai_addr = NULL;
    hint.ai_next = NULL;
    if ((err = getaddrinfo(argv[1], argv[2], &hint, &ailist)) != 0)
        printf("getaddrinfo error: %s", gai_strerror(err));
    for (aip = ailist; aip != NULL; aip = aip->ai_next) {
        print_flags(aip);
        print_family(aip);
        print_type(aip);
        print_protocol(aip);
        printf("\n\thost %s", aip->ai_canonname?aip->ai_canonname:"-");
        if (aip->ai_family == AF_INET) {
            sinp = (struct sockaddr_in *)aip->ai_addr;
            addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf,INET_ADDRSTRLEN);
            printf(" address %s", addr?addr:"unknown");
            printf(" port %d", ntohs(sinp->sin_port));
        }
        printf("\n");
    }
    exit(0);
}

执行:

root@X86-PC:/home/cheney/work-link/linux_C_Test/linux-program/fig16# ./a X86-PC nfs
flags canon family inet type stream protocol TCP
host X86-PC address 127.0.1.1 port 2049
flags canon family inet type datagram protocol UDP
host - address 127.0.1.1 port 2049
root@X86-PC:/home/cheney/work-link/linux_C_Test/linux-program/fig16#

封装

host_serv
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>//结构体addrinfo, in_addr
#include <netinet/in.h>
#include <arpa/inet.h>


struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype)
{
    int				err;
    struct addrinfo	hints, *res;

    bzero(&hints, sizeof(struct addrinfo));
    hints.ai_flags = AI_CANONNAME;	/* always return canonical name */
    hints.ai_family = family;		/* AF_UNSPEC, AF_INET, AF_INET6, etc. */
    hints.ai_socktype = socktype;	/* 0, SOCK_STREAM, SOCK_DGRAM, etc. */

    if ( (err = getaddrinfo(host, serv, &hints, &res)) != 0){
        printf("error %d : %s\n", err, gai_strerror(err));
        return(NULL);
    }


    return(res);	/* return pointer to first on linked list */
}

使用:

int main(int argc, char **argv){
    struct addrinfo *result, *rp;

    struct in_addr addr;

    result = host_serv("www.cnblogs.com", NULL, AF_INET, AF_INET);

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        addr.s_addr = ((struct sockaddr_in*)(rp->ai_addr))->sin_addr.s_addr;
        printf("ip addresss: %s\n", inet_ntoa(addr));
    }

    freeaddrinfo(result);

    return 0;
}
tcp_connect

功能:创建一个TCP套接字,给它绑定一个众所周知的端口,并且允许接收外来的连接请求



#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <zconf.h>

int tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
{
    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_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0){
        printf("tcp_listen error for %s, %s: %s",
                 host, serv, gai_strerror(n));
        exit(0);
    }

    ressave = res;

    do {
        listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
        if (listenfd < 0)
            continue;		/* error, try next one */

        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
            break;			/* success */

        close(listenfd);	/* bind error, close and try next one */
    } while ( (res = res->ai_next) != NULL);

    if (res == NULL){
        printf("tcp_listen error for %s, %s", host, serv);
        exit(0);
    }	/* errno from final socket() or bind() */


    listen(listenfd, 1024);

    if (addrlenp)
        *addrlenp = res->ai_addrlen;	/* return size of protocol address */

    freeaddrinfo(ressave);

    return(listenfd);
}


int Tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
{
    return(tcp_listen(host, serv, addrlenp));
}


//  ./fork_process 127.0.0.1
int main(int argc, char *argv[])
{
    if( argc <= 1 )
    {
        printf( "usage: %s ip_address \n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];  // "127.0.0.1"
    socklen_t addrlen = strlen(ip);
    int socket = Tcp_listen(ip, NULL, &addrlen);
    printf("%d", socket);

    return 0;
}
udp_connect

getsockname

#include <netinet/in.h>
#include <sys/un.h>

typedef union {
    struct sockaddr_storage ss;
    struct sockaddr_in6 in6;
    struct sockaddr_in in;
    struct sockaddr_un un;
    struct sockaddr sa;
} ACL_SOCKADDR;
# define	ACL_SOCKET		int
# define	ACL_SOCKET_INVALID	(int) -1
/**
 * 取得套接字的类型:是网络套接字还是域套接字
 * @param fd {ACL_SOCKET} 网络套接字
 * @return {int} -1: 表示出错或输入非法或非套接字; >= 0 表示成功获得套接字
 *  类型,返回值有 AF_INET、AF_INET6 或 AF_UNIX
 */
int acl_getsocktype(ACL_SOCKET fd)
{
    ACL_SOCKADDR addr;
    struct sockaddr *sa = (struct sockaddr*) &addr;
    socklen_t len = sizeof(addr);

    if (fd == ACL_SOCKET_INVALID) {
        return -1;
    }

    if (getsockname(fd, sa, &len) == -1) {
        return -1;
    }

    if (sa->sa_family == AF_UNIX) {
        return AF_UNIX;
    }
    
    if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) {
        return sa->sa_family;
    }
    return -1;
}

抓包分析dns的原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值