当前位置:首页 > 行业动态 > 正文

c 如何获取内网的ip地址

在Windows系统中,可以通过命令提示符输入 ipconfig命令来获取内网IP地址;在Linux或macOS系统中,可使用 ifconfigip addr命令查看。

在C语言中获取内网IP地址是一个常见需求,特别是在网络编程和系统管理中,下面将详细介绍几种常用的方法来实现这一目标,包括使用gethostnamegethostbyname函数、getifaddrs函数以及通过套接字接口查询等。

一、使用gethostnamegethostbyname函数

原理

这种方法首先通过gethostname函数获取当前主机的主机名,然后使用gethostbyname函数将主机名解析为IP地址。

实现步骤

获取主机名:调用gethostname函数,将主机名存储在一个字符数组中。

解析主机名:使用gethostbyname函数将主机名解析为一个hostent结构体,该结构体包含了主机的IP地址信息。

转换为点分十进制格式:利用inet_ntoa函数将网络字节序的IP地址转换为点分十进制格式的字符串。

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>
int main() {
    char hostname[256];
    if (gethostname(hostname, sizeof(hostname)) == -1) {
        perror("gethostname");
        exit(EXIT_FAILURE);
    }
    struct hostent *host_entry = gethostbyname(hostname);
    if (host_entry == NULL) {
        herror("gethostbyname");
        exit(EXIT_FAILURE);
    }
    char *IPbuffer = inet_ntoa(*((struct in_addr*) host_entry->h_addr_list[0]));
    printf("本地IP地址: %s
", IPbuffer);
    return 0;
}

二、使用`getifaddrs`函数

原理

getifaddrs函数可以获取所有网络接口的详细信息,包括每个接口的IP地址。

实现步骤

引入头文件:包含必要的头文件,如<ifaddrs.h><netinet/in.h><arpa/inet.h>等。

定义结构体指针:定义一个指向ifaddrs结构体的指针,用于存储接口信息。

获取并遍历接口信息:调用getifaddrs函数获取所有接口的信息,并遍历链表打印每个接口的IP地址,对于IPv4地址,可以通过检查ifa_addr->sa_family是否等于AF_INET来判断。

释放内存:使用完毕后,调用freeifaddrs函数释放分配的内存。

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main() {
    struct ifaddrs *ifap, *ifa;
    if (getifaddrs(&ifap) == -1) {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }
    for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr->sa_family == AF_INET) { // 只处理IPv4地址
            char host[NI_MAXHOST];
            if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0) {
                printf("接口: %st地址: %s
", ifa->ifa_name, host);
            }
        }
    }
    freeifaddrs(ifap);
    return 0;
}

三、通过套接字接口查询

原理

创建UDP套接字并连接到一个外部地址(如网关或DNS服务器),然后查询套接字的本地地址,这种方法适用于需要获取特定网络接口上的IP地址的情况。

实现步骤

创建套接字:使用socket函数创建一个UDP套接字。

设置服务器地址:定义一个sockaddr_in结构体作为服务器地址,通常设置为一个已知的外部地址。

连接套接字:调用connect函数将套接字连接到服务器地址。

获取本地地址:使用getsockname函数获取套接字的本地地址,即绑定到套接字的网络接口的IP地址。

关闭套接字:关闭套接字以释放资源。

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(80); // 任意选择一个端口号
    inet_pton(AF_INET, "8.8.8.8", &serv_addr.sin_addr); // 使用Google的DNS服务器地址作为测试地址
    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("connect");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    struct sockaddr_in local_addr;
    socklen_t addr_len = sizeof(local_addr);
    if (getsockname(sockfd, (struct sockaddr *)&local_addr, &addr_len) == -1) {
        perror("getsockname");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    char local_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &local_addr.sin_addr, local_ip, INET_ADDRSTRLEN);
    printf("本地IP地址: %s
", local_ip);
    close(sockfd);
    return 0;
}

四、FAQs

Q1: 如何过滤掉不需要的网络接口?

A1: 在使用getifaddrs函数时,可以通过检查接口名称来过滤掉不需要的网络接口,如果要过滤掉回环接口(lo),可以在遍历链表时加入条件判断,跳过名称为"lo"的接口,示例如下:

for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
    if (strcmp(ifa->ifa_name, "lo") == 0) continue; // 跳过本地回环接口
    // 其他处理逻辑...
}

Q2: 如何同时获取IPv4和IPv6地址?

A2: 在使用getifaddrs函数时,只需检查ifa_addr->sa_family字段是否等于AF_INET(表示IPv4)或AF_INET6(表示IPv6),如果需要同时获取两种类型的地址,可以在遍历链表时分别处理,示例如下:

for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
    if (ifa->ifa_addr == NULL) continue;
    if (ifa->ifa_addr->sa_family == AF_INET) { // IPv4地址处理逻辑 } else if (ifa->ifa_addr->sa_family == AF_INET6) { // IPv6地址处理逻辑 }
}
0