在Linux环境下使用C语言获取IP地址是网络编程中的常见需求,通常涉及系统调用和套接字编程,以下是几种主流的实现方式,涵盖本地IP和远程IP的获取方法,并提供关键代码示例和注意事项。

获取本地主机IP地址
获取本地主机IP地址通常需要结合gethostname()和gethostbyname()函数,或使用更现代的getaddrinfo()接口,前者在IPv4环境下较为简单,后者则支持IPv4和IPv6双栈。
使用gethostname()和gethostbyname()
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
int main() {
char hostname[1024];
struct hostent *host;
if (gethostname(hostname, sizeof(hostname)) == -1) {
perror("gethostname");
exit(EXIT_FAILURE);
}
host = gethostbyname(hostname);
if (host == NULL) {
herror("gethostbyname");
exit(EXIT_FAILURE);
}
printf("Local IP addresses:\n");
for (int i = 0; host->h_addr_list[i] != NULL; i++) {
struct in_addr addr;
memcpy(&addr, host->h_addr_list[i], sizeof(struct in_addr));
printf("%s\n", inet_ntoa(addr));
}
return 0;
}
注意事项:gethostbyname()已被标记为过时(deprecated),且不支持IPv6,实际开发中建议优先使用getaddrinfo()。

使用getaddrinfo()(推荐)
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
int main() {
struct addrinfo hints, *res, *p;
char ipstr[INET6_ADDRSTRLEN];
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET或AF_INET6强制指定版本
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo("localhost", NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
printf("Local IP addresses:\n");
for (p = res; p != NULL; p = p->ai_next) {
void *addr;
const char *ipver;
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("%s: %s\n", ipver, ipstr);
}
freeaddrinfo(res);
return 0;
}
获取远程主机IP地址
获取远程主机IP地址通常通过域名解析实现,getaddrinfo()是最佳选择,它能够处理IPv4/IPv6并自动解析DNS。
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <hostname>\n", argv[0]);
return 1;
}
struct addrinfo hints, *res, *p;
char ipstr[INET6_ADDRSTRLEN];
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 1;
}
printf("IP addresses for %s:\n", argv[1]);
for (p = res; p != NULL; p = p->ai_next) {
void *addr;
const char *ipver;
if (p->ai_family == AF_INET) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("%s: %s\n", ipver, ipstr);
}
freeaddrinfo(res);
return 0;
}
通过套接字获取网络接口IP
若需获取特定网络接口(如eth0)的IP地址,可以使用ioctl()结合struct ifreq,但此方法需要管理员权限且仅适用于IPv4。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if.h>
#include <sys/ioctl.h>
int main() {
int sockfd;
struct ifreq ifr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
strncpy(ifr.ifr_name, "eth0", IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFADDR, &ifr) == -1) {
perror("ioctl");
close(sockfd);
exit(EXIT_FAILURE);
}
struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;
printf("Interface eth0 IP: %s\n", inet_ntoa(addr->sin_addr));
close(sockfd);
return 0;
}
编译与运行注意事项
- 编译选项:使用
gcc -o program program.c编译,若涉及<linux/if.h>等头文件,可能需要链接-lm或-lnsl库。 - 权限问题:
ioctl()方法需要root权限,而getaddrinfo()无需特殊权限。 - 错误处理:实际开发中需全面检查函数返回值,避免内存泄漏(如
freeaddrinfo()的调用)。
通过以上方法,开发者可根据实际需求选择合适的IP获取策略,现代Linux网络编程推荐优先使用getaddrinfo(),其兼容性和功能性更优,同时注意处理IPv4/IPv6双栈场景。

















