【Linux C | 网络编程】getaddrinfo 函数详解及C语言例子

2024-03-01 16:44

本文主要是介绍【Linux C | 网络编程】getaddrinfo 函数详解及C语言例子,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 getaddrinfo 函数 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:2024-03-01 14:15:54

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
  • 🎄二、getaddrinfo 函数
    • ✨2.1 getaddrinfo 函数介绍
    • ✨2.2 struct addrinfo 结构体说明
  • 🎄三、gai_strerror、freeaddrinfo 函数
    • ✨3.1 gai_strerror 函数介绍
    • ✨3.2 freeaddrinfo 函数介绍
  • 🎄四、getaddrinfo 函数使用例子
  • 🎄五、总结


在这里插入图片描述

🎄一、概述

前面介绍过域名和IP地址之间转换的两个函数:gethostbynamegethostbyaddr,但是这两个函数仅仅支持IPv4。本文再介绍一个可支持 IPv4 和 IPv6 的函数getaddrinfo,该函数可以处理名字到地址以及服务到端口这两种转换。


在这里插入图片描述

🎄二、getaddrinfo 函数

✨2.1 getaddrinfo 函数介绍

  • 1、函数原型:

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints, struct addrinfo **res);
    void freeaddrinfo(struct addrinfo *res);
    const char *gai_strerror(int errcode);
    
  • 2、函数描述:
    getaddrinfo函数根据给定的主机名和服务名,返回一个struct addrinfo结构链表,每个struct addrinfo结构都包含一个互联网地址。getaddrinfo函数将gethostbynamegetservbyname函数提供的功能组合到一个接口中,但与后一个函数不同,getaddrinfo是可重入的,可支持IPv4、IPv6。

  • 3、函数参数:

    • node:一个主机名或地址串( IPv4的点分十进制数串或IPv6的十六进制数串)。如果hints.ai_flags包含AI_NUMERICHOST标志,则此参数必须是IP地址字符串;

    • service:一个服务名或十进制端口号数串。如果此参数被设置为一个服务名称,则会将其转换为相应的端口号。如果设置为NULL,则返回的套接字地址的端口号将保持未初始化状态。如果在hints.ai_flags中指定了AI_NUMERICSERV,并且此参数不为NULL,则此参数必须指向包含数字端口号的字符串;

    • hints:hints参数可以是一个空指针,也可以是一个指向某个addrinfo结构的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。hints参数中,调用者可以设置的字段有:ai_flags、ai_family、ai_socktype、ai_protocol。其中,ai_flags取值如下表:

      取值说明
      AI_PASSIVE套接字将用于被动打开.
      AI_CANONNAME告知getaddrinfo函数返回主机的规范名字.
      AI_NUMERICHOST防止任何类型的名字到地址映射,hostname参数必须是一个地址串。
      AI_NUMERICSERV防止任何类型的名字到服务映射, service参数必须是一个十进制端口号。AI__V4MAPPED
      AI_V4MAPPED如果同时指定ai_family成员的值为AF_INET6,那么如果没有可用的AAAA记录,就返回与A记录对应的IPv4映射的IPv6地址。
      AI_ALL如果同时指定AI_V4MAPPED标志,那么除了返回与AAAA记录对应的IPv6地址外,还返回与A记录对应的IPv4映射的IPv6地址。
      AI_ADDRCONFIG按照所在主机的配置选择返回地址类型,也就是只查找与所在主机回馈接口以外的网络接口配置的IP地址版本一致的地址。

      ai_family 取值一般为AF_XXX,例如:AF_INETAF_INET6AF_UNSPEC(不限制IP地址协议);
      ai_socktype 取值一般为SOCK_XXX,例如:SOCK_STREAMSOCK_DGRAM
      ai_protocol 字段指定返回的套接字地址的协议。在该字段中指定0表示getaddrinfo函数可以返回具有任何协议的套接字地址。

    • res:传出参数,如果本函数返回成功0,则 res 参数指向的变量已被填入一个指针,它指向的是由其中的 ai_next 成员串接起来的 addrinfo 结构链表。

  • 4、返回值:
    成功返回0,失败返回非0,取值如下表:

    常值说明
    EAI_AGAIN名字解析中临时失败
    EAI_BADFLAGSai_flags的值无效
    EAI_FAIL名字解析中不可恢复地失败
    EAI_FAMILY不支持ai_family
    EAI_MEMORY内存分配失败
    EAI_NONAMEhostname或service未提供,或者不可知
    EAI_OVERFTOW用户参数缓冲区溢出(仅限getnameinfo( )函数)
    EAI_SERVICE不支持ai_socktype类型的service
    EAI_SOCKTYPE不支持ai_socktype
    EAI_SYSTEM在errno变量中有系统错误返回

✨2.2 struct addrinfo 结构体说明

struct addrinfo结构体定义在头文件 netdb.h 中,结构体声明如下:

struct addrinfo {int              ai_flags;int              ai_family;int              ai_socktype;int              ai_protocol;socklen_t        ai_addrlen;struct sockaddr *ai_addr;char            *ai_canonname;struct addrinfo *ai_next;
};

在这里插入图片描述
结构体字段说明:

  • ai_flags:标志,在调用时使用,具体取值见上面 hints参数 的说明;
  • ai_family:IP协议族,一般取值有AF_INETAF_INET6AF_UNSPEC(不限制IP地址协议);
  • ai_socktype:socket类型,取值一般为SOCK_XXX,例如:SOCK_STREAMSOCK_DGRAM
  • ai_protocol:此字段指定返回的套接字地址的协议,一般有IPPROTO_UDPIPPROTO_TCP
  • ai_addrlen:返回的地址结构体ai_addr的长度。一般IPv4是4,IPv6是16;
  • ai_addr:存储IP地址数据,一般转换成struct sockaddr_instruct sockaddr_in6使用;
  • ai_canonname:正式的、标准的名称;
  • ai_next:用作链表结点指针,指向下一个struct addrinfo结点。

在这里插入图片描述

🎄三、gai_strerror、freeaddrinfo 函数

在使用 getaddrinfo 函数时,还有两个函数也会使用到,下面简单介绍一下这两个函数。

✨3.1 gai_strerror 函数介绍

  • 1、函数原型:
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    const char *gai_strerror(int errcode);
    
  • 2、函数描述:
    getaddrinfo出错会返回的非0错误值,gai_strerror以这些值为它的唯一参数,返回一个指向对应的出错信息串的指针。
    常值说明
    EAI_AGAIN名字解析中临时失败
    EAI_BADFLAGSai_flags的值无效
    EAI_FAIL名字解析中不可恢复地失败
    EAI_FAMILY不支持ai_family
    EAI_MEMORY内存分配失败
    EAI_NONAMEhostname或service未提供,或者不可知
    EAI_OVERFTOW用户参数缓冲区溢出(仅限getnameinfo( )函数)
    EAI_SERVICE不支持ai_socktype类型的service
    EAI_SOCKTYPE不支持ai_socktype
    EAI_SYSTEM在errno变量中有系统错误返回

✨3.2 freeaddrinfo 函数介绍

  • 1、函数原型:

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    void freeaddrinfo(struct addrinfo *res);
    
  • 2、函数描述:
    由getaddrinfo返回的所有存储空间都是动态获取的(譬如来自malloc调用),包括addrinfo结构、ai_addr结构和ai_canonname字符串。这些存储空间需要通过调用freeaddrinfo返还给系统。

  • 3、参数
    res:res参数应指向由getaddrinfo返回的第一个addrinfo结构。这个链表中的所有结构以及由它们指向的任何动态存储空间(譬如套接字地址结构和规范主机名)都被释放掉。

  • 4、注意:
    如果getaddrinfo成功返回后,我们为了保存返回的信息而仅仅复制了返回的addrinfo结构,在调用freeaddrinfo后,就会存在一个错误:我们前面复制的addrinfo结构中的指针指向的内存空间已在调用freeaddrinfo后返还给系统。
    这种只复制结构体而没复制结构体字段指向的内容的方式称为浅复制;复制结构体又复制结构体字段指向的内容的方式称为深复制。上面例子中,如果确实要保存信息,可以使用深复制来保存。


在这里插入图片描述

🎄四、getaddrinfo 函数使用例子

// getaddrinfo_sample.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>int main(int argc, char *argv[]) {struct addrinfo hints, *result, *rp;// 定义addrinfo结构体变量int err;                         	// getaddrinfo函数返回值char ipstr[INET6_ADDRSTRLEN];       // 存储IP地址字符串的缓冲区if (argc != 2) {                    // 检查命令行参数数量是否正确fprintf(stderr, "Usage: %s hostname\n", argv[0]);return -1;}memset(&hints, 0, sizeof(hints));  	// 初始化hints结构体hints.ai_family = AF_UNSPEC;		// 不限制IP地址版本hints.ai_socktype = SOCK_STREAM;	// 使用TCP协议if ((err = getaddrinfo(argv[1], NULL, &hints, &result)) != 0) {  // 解析主机名并将结果存储在result指针中fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(err));return -1;}printf("IP addresses for %s:\n", argv[1]);for (rp = result; rp != NULL; rp = rp->ai_next) {	// 遍历result指针中的所有套接字地址结构void *addr;char *ipver;if (rp->ai_family == AF_INET) {	// IPv4地址struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;addr = &(ipv4->sin_addr);ipver = "IPv4";} else { 						// IPv6地址struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;addr = &(ipv6->sin6_addr);ipver = "IPv6";}inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr));  // 将套接字地址结构转换为IP地址字符串printf("  %s: %s\n", ipver, ipstr);                    // 打印IP地址和版本号}freeaddrinfo(result);   // 释放由getaddrinfo函数分配的内存return 0;    // 程序正常退出
}

运行结果:
下面是分别查询www.baidu.com(百度)、www.goolge.com(谷歌)、blog.csdn.net(CSDN)、localhost(本地主机名)、ip6-localhostip6-localnet的打印结果。
在这里插入图片描述

localhost(本地主机名)、ip6-localhostip6-localnet,这三个在/etc/hosts文件中有说明,可以发现与查询的一致。
在这里插入图片描述


在这里插入图片描述

🎄五、总结

👉本文重点介绍了 getaddrinfo、freeaddrinfo、gai_strerror 三个函数,并给出C语言使用例子。

通过本文的介绍,我们深入探讨了 Linux 系统中 getaddrinfo 函数的定义和使用场景。getaddrinfo 函数在网络编程中扮演着重要角色,允许开发人员根据主机名和服务名动态获取地址信息,为构建灵活且健壮的网络应用提供了便利。希望本文能帮助您更好地了解并应用 getaddrinfo 函数。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
1、Linux的man手册
2、《Unix网络编程卷1》

这篇关于【Linux C | 网络编程】getaddrinfo 函数详解及C语言例子的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763116

相关文章

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

《SpringBoot整合mybatisPlus实现批量插入并获取ID详解》这篇文章主要为大家详细介绍了SpringBoot如何整合mybatisPlus实现批量插入并获取ID,文中的示例代码讲解详细... 目录【1】saveBATch(一万条数据总耗时:2478ms)【2】集合方式foreach(一万条数

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

Python装饰器之类装饰器详解

《Python装饰器之类装饰器详解》本文将详细介绍Python中类装饰器的概念、使用方法以及应用场景,并通过一个综合详细的例子展示如何使用类装饰器,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. 引言2. 装饰器的基本概念2.1. 函数装饰器复习2.2 类装饰器的定义和使用3. 类装饰

MySQL 中的 JSON 查询案例详解

《MySQL中的JSON查询案例详解》:本文主要介绍MySQL的JSON查询的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录mysql 的 jsON 路径格式基本结构路径组件详解特殊语法元素实际示例简单路径复杂路径简写操作符注意MySQL 的 J

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

Python ZIP文件操作技巧详解

《PythonZIP文件操作技巧详解》在数据处理和系统开发中,ZIP文件操作是开发者必须掌握的核心技能,Python标准库提供的zipfile模块以简洁的API和跨平台特性,成为处理ZIP文件的首选... 目录一、ZIP文件操作基础三板斧1.1 创建压缩包1.2 解压操作1.3 文件遍历与信息获取二、进阶技

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Java中的@SneakyThrows注解用法详解

《Java中的@SneakyThrows注解用法详解》:本文主要介绍Java中的@SneakyThrows注解用法的相关资料,Lombok的@SneakyThrows注解简化了Java方法中的异常... 目录前言一、@SneakyThrows 简介1.1 什么是 Lombok?二、@SneakyThrows