在 Linux 中,使用 man 7 netdevice 描述您可以使用的接口,因为 Anish Goyal 已经回答。
这是非常简单和健壮的,并且使用很少的资源(因为它只是几个系统调用)。不幸的是,它是特定于 Linux 的,并且使代码不可移植。
可以便携地执行此操作。我在这里描述了可移植选项,因为特定于 Linux 的选项相当微不足道。尽管仅获取本地主机 IP 地址的方法相当复杂,但该模式非常有用,因为它可以隐藏特定于系统的怪癖,同时允许系统管理员轻松自定义行为。
可移植解决方案的想法是,您使用一个小的帮助程序或 shell 脚本来获取信息,并让它以某种易于解析的格式将信息输出到您的主程序。如果您的应用程序被命名为yourapp,则通常在/usr/lib/yourapp/ 中安装此类帮助程序,例如/usr/lib/yourapp/local-ip-addresses。
就我个人而言,我建议使用 shell 脚本(以便系统管理员可以在需要自定义行为时轻松编辑帮助程序)和输出格式,其中每个接口都在自己的行上,字段用空格分隔,也许
inet 接口名称 ipv4 地址 [ 主机名 ]*
inet6 接口名称 ipv6 地址 [ 主机名 ]*
即第一个记号指定地址族,第二个记号指定接口名称,第三个记号指定地址,可选地后跟与该地址对应的主机名或别名。
对于辅助程序/shell脚本本身,有两种基本的做法:
-
一次性
例如,在 Linux 中解析 LANG=C LC_ALL=C ip address 输出。
打印地址后程序/脚本将退出。
-
连续
程序/脚本将打印 IP 地址信息,但不会退出,只要管道保持打开状态,它就会运行,并在接口关闭或启动时提供更新。
在 Linux 中,程序/脚本可以使用 DBUS 或 NetworkManager 来等待此类事件;它不需要轮询(即重复检查命令输出)。
shell 脚本的额外好处是它可以同时支持多个发行版,甚至操作系统(至少跨 POSIX 系统)。此类脚本通常具有类似的大纲:
#!/bin/sh
export LANG=C LC_ALL=C
case "$(uname -s)" in
Linux)
if [ -x /bin/ip ]; then
/bin/ip -o address | awk \
'/^[0-9]*:/ {
addr = $4
sub(/\/.*$/, "", addr)
printf "%s %s %s\n", $3, $2, addr
}'
exit 0
elif [ -x /sbin/ifconfig ]; then
/sbin/ifconfig | awk \
'BEGIN {
RS = "[\t\v\f\r ]*\n"
FS = "[\t\v\f ]+"
}
/^[0-9A-Za-z]/ {
iface = $1
}
/^[\t\v\f ]/ {
if (length(iface) > 0)
for (i = 1; i < NF-1; i++)
if ($i == "inet") {
addr = $(i+1)
sub(/^addr:/, "", addr)
printf "inet %s %s\n", iface, addr
} else
if ($i == "inet6") {
addr = $(i+2)
sub(/\/.*$/, "", addr)
printf "inet6 %s %s\n", iface, addr
}
}'
exit 0
fi
;;
# Other systems?
esac
printf 'Cannot determine local IP addresses!\n'
exit 1
脚本将语言环境设置为C/POSIX,因此外部命令的输出将采用默认语言环境(英语等)。 uname -s 提供内核名称(Linux 用于 Linux),并且可以使用例如[ shell 命令。
我只为 Linux 实现了 scriptlet,因为那是我现在使用的机器。 (ip 和 ifconfig 替代方案都可以在我的机器上工作,并提供相同的输出——尽管你不能期望以任何特定的顺序获得接口。)
系统管理员可能需要编辑此特定帮助程序脚本的情况包括尚不受支持的系统、具有新核心工具的系统以及具有应从正常接口列表中排除的接口的系统(例如,连接到为 DNS、文件服务器、备份、LDAP 等特权目的而保留的内部子网。
在 C 应用程序中,您只需使用 popen("/usr/lib/yourapp/local-ip-addresses", "r") 执行外部程序或 shell 脚本,它会为您提供一个 FILE *,您可以像读取文件一样读取它(除非您无法查找或回退它)。从管道中读取所有内容后,pclose() 句柄:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* ... */
FILE *in;
char *line_ptr = NULL;
size_t line_size = 0;
ssize_t line_len;
int status;
in = popen("/usr/lib/myapp/local-ip-addresses", "r");
if (!in) {
fprintf(stderr, "Cannot determine local IP addresses: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
while (1) {
line_len = getline(&line_ptr, &line_size, in);
if (line_len < 1)
break;
/* Parse line_ptr */
}
free(line_ptr);
line_ptr = NULL;
line_size = 0;
if (ferror(in) || !feof(in)) {
pclose(in);
fprintf(stderr, "Read error while obtaining local IP addresses.\n");
exit(EXIT_FAILURE);
}
status = pclose(in);
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
fprintf(stderr, "Helper utility /usr/lib/myapp/local-ip-addresses failed to obtain local IP addresses.\n");
exit(EXIT_FAILURE);
}
我省略了解析代码,因为有很多选择——strtok()、sscanf(),甚至是一个将行拆分为标记(并填充指针数组)的自定义函数——还有我自己的首选option 是最不受欢迎的一个(最后一个,自定义函数)。
虽然仅此任务所需的代码几乎不值得花时间,但使用这种方法的应用程序往往会使用几个这样的帮助程序。然后,当然,以一种易于解析但支持所有用例的方式选择管道数据格式是有意义的。摊余成本也更容易接受。