【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

相关文章

Oracle的to_date()函数详解

《Oracle的to_date()函数详解》Oracle的to_date()函数用于日期格式转换,需要注意Oracle中不区分大小写的MM和mm格式代码,应使用mi代替分钟,此外,Oracle还支持毫... 目录oracle的to_date()函数一.在使用Oracle的to_date函数来做日期转换二.日

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后

Linux中chmod权限设置方式

《Linux中chmod权限设置方式》本文介绍了Linux系统中文件和目录权限的设置方法,包括chmod、chown和chgrp命令的使用,以及权限模式和符号模式的详细说明,通过这些命令,用户可以灵活... 目录设置基本权限命令:chmod1、权限介绍2、chmod命令常见用法和示例3、文件权限详解4、ch

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服

详解Java中的敏感信息处理

《详解Java中的敏感信息处理》平时开发中常常会遇到像用户的手机号、姓名、身份证等敏感信息需要处理,这篇文章主要为大家整理了一些常用的方法,希望对大家有所帮助... 目录前后端传输AES 对称加密RSA 非对称加密混合加密数据库加密MD5 + Salt/SHA + SaltAES 加密平时开发中遇到像用户的

什么是cron? Linux系统下Cron定时任务使用指南

《什么是cron?Linux系统下Cron定时任务使用指南》在日常的Linux系统管理和维护中,定时执行任务是非常常见的需求,你可能需要每天执行备份任务、清理系统日志或运行特定的脚本,而不想每天... 在管理 linux 服务器的过程中,总有一些任务需要我们定期或重复执行。就比如备份任务,通常会选在服务器资