Stack overflow地址:c++ - How to detect IP address change programmatically in Linux? - Stack Overflow
翻译:
有监测本机 IP地址修改的,使用C++在Linux上编程的方法吗?
Answers1:
在C语言中,我使用的获取当前IP的方法:
int s;
struct ifreq ifr = {};
s = socket(PF_INET, SOCK_DGRAM, 0);
strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name));
if (ioctl(s, SIOCGIFADDR, &ifr) >= 0)
printf("%s\n",
inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
使用你感兴趣的网卡代替"eth0"。你需要做的就是轮询检查网卡IP是否有改变。
Answers2:
你来这里,这不需要轮询。
它只监听了 RTM_NEWADDR,但是它是很容易修改支持 RTM_DELADDR,如果你需要的话。
#include
#include
#include
#include
#include
#include
int main()
{
struct sockaddr_nl addr;
int sock, len;
char buffer[4096];
struct nlmsghdr *nlh;
if ((sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
perror("couldn't open NETLINK_ROUTE socket");
return 1;
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("couldn't bind");
return 1;
}
nlh = (struct nlmsghdr *)buffer;
while ((len = recv(sock, nlh, 4096, 0)) > 0) {
while ((NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE)) {
if (nlh->nlmsg_type == RTM_NEWADDR) {
struct ifaddrmsg *ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh);
struct rtattr *rth = IFA_RTA(ifa);
int rtl = IFA_PAYLOAD(nlh);
while (rtl && RTA_OK(rth, rtl)) {
if (rth->rta_type == IFA_LOCAL) {
uint32_t ipaddr = htonl(*((uint32_t *)RTA_DATA(rth)));
char name[IFNAMSIZ];
if_indextoname(ifa->ifa_index, name);
printf("%s is now %d.%d.%d.%d\n",
name,
(ipaddr >> 24) & 0xff,
(ipaddr >> 16) & 0xff,
(ipaddr >> 8) & 0xff,
ipaddr & 0xff);
}
rth = RTA_NEXT(rth, rtl);
}
}
nlh = NLMSG_NEXT(nlh, len);
}
}
return 0;
}
Answers3:
没有一种很容易的方式。每个Linux发行版使用不同的地方存储IP地址等(如果你考虑UNIX的变体将会有更多的版本)。例如,你可以使用,/sbin/ifconfig来获取接口的IP地址,但是你甚至不能确定你能找到它。
或者,给你一个可执行程序,你不得不建立一个线程在一定的时间周期内(比如5秒)获取数据,并中断进行输出。例如,如果你有桥梁,这可能会有所不同,但是这不是很容易的。
我想到的另一个解决方案是,如果你有机会使用 GNOME或者其他像 KDE的发行版本,你可以依赖它们提供的消息/信息。例如,NetworkManager输出一个信号到 DBUS standard bus,当设备改变时。你需要为这些信号添加一个监听器,监听这里的信息(不立即工作,在这里是一个缓存)。请注意,当一个新接口添加或者其中一个接口修改IP地址时会有不同的消息。这是我现在能想到的最好的方法。
Answers4:
如果你得用户使用 NetworkManager,你可以轮询 NetworkManager.Connection.Active和 NetworkManager.IP4Config通过 D-Bus,获取更多确定信息。
Answers5:
如果 iproute2安装了,你可以在 2.6版本的内核上这么做:
/sbin/ip monitor
如果本地网卡的状态和地址改变的话,会输出到控制台上。你的程序可以读这个信息。
你也可以使用其他类似 iproute2的低等级机制(我认为它是一个 netlink套接字)。
Answers6:
ste's建议使用 ioctl,使用 SIOCGIFADDR是技术正确的,不幸的是,它是不可行的对于现代的 Linux操作系统,这里一个网卡可以拥有多个地址在不使用子网卡的情况下(例如:eth0:1),就像现在已经过时的ifconfig所做的那样。
你最好使用 getifaddrs(3),这是 glibc2.3提供的: http://www.kernel.org/doc/man-pages/online/pages/man3/getifaddrs.3.html
不幸的是它效率不高(你会得到所有网卡的一个链表,并且不得不遍历它找到那个你想要的网卡),但是大多数情况下你检查它不超过一分钟一次,这是可以接受的。
Answers7:
一个方法是写一个计划任务,它包含调用 gethost family的相关库函数。如果你使用 gethostbyname()你可以比较返回值中的 h_addr_list。具体细节看 man gethostbyname。
如果你想要在你的程序中做这件事,创建一个线程做相同的事,然后睡眠一定的时间。
Answers8:
从 rtnetlink的man页引用:
描述:
Rtnetlink允许读取并修改内核路由表。它在内核中使用并在不同的子系统中通信,尽管它的用户空间通信的使用没有文档。网络路由,IP地址,link参数,相关设置,排队规则,流量类别和分组类别都可以被 NETLINK_ROUTE控制。它基于 netlink消息,看 netlink(7)获取更多的消息。
Answers9:
完整的C语言测试示例,并在不同的线程中进行通知:
#include // AF_INET, socket(), bind()
#include // struct ifaddrs, getifaddrs()
#include // struct sockaddr_in
#include // inet_ntoa(), htonl()
#include // if_indextoname()
#include
#include
#include
#include
#include
typedef enum {
IP_ADDR_ADD,
IP_ADDR_REMOVE
} ip_address_change_notification_type_t;
typedef void (*ip_address_change_notification_callback_t)(ip_address_change_notification_type_t type, uint32_t ipaddr, void *userdata);
static int ip_address_change_notification_socket = -1;
static pthread_t ip_address_change_notification_thread;
static ip_address_change_notification_callback_t ip_address_change_notification_callback;
static void *ip_address_change_notification_callback_userdata;
void *ip_address_change_notification_worker(void *arg)
{
fprintf(stderr, "ip_address_change_notification_worker entered.\n");
if (ip_address_change_notification_socket == -1) {
goto done;
}
char buffer[4096];
struct nlmsghdr *nlh = (struct nlmsghdr *)buffer;
int len;
while ((len = recv(ip_address_change_notification_socket, nlh, sizeof(buffer), 0)) > 0) {
for (; (NLMSG_OK(nlh, len)) && (nlh->nlmsg_type != NLMSG_DONE); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == RTM_NEWADDR) {
fprintf(stderr, "Netlink: RTM_NEWADDR\n");
} else if (nlh->nlmsg_type == RTM_DELADDR) {
fprintf(stderr, "Netlink: RTM_DELADDR\n");
} else {
fprintf(stderr, "Netlink: nlmsg_type=%d\n", nlh->nlmsg_type);
continue; // Some other kind of announcement.
}
struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(nlh);
struct rtattr *rth = IFA_RTA(ifa);
int rtl = IFA_PAYLOAD(nlh);
for (; rtl && RTA_OK(rth, rtl); rth = RTA_NEXT(rth,rtl)) {
char name[IFNAMSIZ];
uint32_t ipaddr;
if (rth->rta_type != IFA_LOCAL) continue;
ipaddr = *((uint32_t *)RTA_DATA(rth)); // In network byte-order.
fprintf(stderr, "Interface %s %s has IP address %s\n", if_indextoname(ifa->ifa_index, name), (nlh->nlmsg_type == RTM_NEWADDR ? "now" : "no longer"), inet_ntoa(*((struct in_addr *)&ipaddr)));
if (ip_address_change_notification_callback) (*ip_address_change_notification_callback)((nlh->nlmsg_type == RTM_NEWADDR ? IP_ADDR_ADD : IP_ADDR_REMOVE), ipaddr, ip_address_change_notification_callback_userdata);
}
}
}
done:
fprintf(stderr, "ip_address_change_notification_worker exited.\n");
return (NULL);
}
bool begin_ip_address_change_notifications(ip_address_change_notification_callback_t callback, void *userdata)
{
if (ip_address_change_notification_socket != -1) return false;
ip_address_change_notification_callback = callback;
ip_address_change_notification_callback_userdata = userdata;
if ((ip_address_change_notification_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
perror("begin_ip_address_change_notifications socket");
return false;
}
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_IPV4_IFADDR;
if (bind(ip_address_change_notification_socket, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("begin_ip_address_change_notifications bind");
goto bail;
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, 1); // Preclude the need to do pthread_join on the thread after it exits.
int err = pthread_create(&ip_address_change_notification_thread, &attr, ip_address_change_notification_worker, NULL);
pthread_attr_destroy(&attr);
if (err != 0) {
fprintf(stderr, "Error creating ip address change notification thread.\n");
goto bail;
}
return (true);
bail:
close(ip_address_change_notification_socket);
ip_address_change_notification_socket = -1;
ip_address_change_notification_callback = NULL;
ip_address_change_notification_callback_userdata = NULL;
return false;
}
void end_ip_address_change_notifications(void)
{
if (ip_address_change_notification_socket == -1) return;
pthread_cancel(ip_address_change_notification_thread);
close(ip_address_change_notification_socket);
ip_address_change_notification_socket = -1;
ip_address_change_notification_callback = NULL;
ip_address_change_notification_callback_userdata = NULL;
}