方法一:
參考:https://blog.csdn.net/hbk320/article/details/47300067
由於linux下的ifconfig命令就能夠實現在應用層監控網線插拔狀態,例如當網線連接正常時,使用ifconfig eth0命令,打印的信息中會有RUNNING,而拔掉網線後,再使用ifconfig eth0命令,RUNNING就不見了。所以,實現Linux應用層監控網線插入狀態就相當於自己寫一個ifconfig函數。
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <arpa/inet.h>
int net_get_gateway(char *gateway)
{
FILE *fp = NULL;
char buf[256];
char cmd[64];
char *tmp;
strcpy(cmd, "ip route");
fp = popen(cmd, "r");
if(NULL == fp)
{
perror("popen error");
return -1;
}
while(fgets(buf, sizeof(buf), fp) != NULL)
{
tmp = buf;
// 例如: default via 192.168.2.1 dev eth0
// 找到default的起始點
while(*tmp && isspace(*tmp))
{
++ tmp;
}
if(strncmp(tmp, "default", strlen("default")) == 0)
break;
}
// %*s表示以空格爲分隔符, 字符串將被忽略掉
sscanf(buf, "%*s%*s%s", gateway);
pclose(fp);
return 0;
}
int net_detect(char* net_name)
{
int skfd = 0;
struct ifreq ifr;
struct sockaddr_in *pAddr = NULL;
skfd = socket(AF_INET, SOCK_DGRAM, 0);
if(skfd < 0)
{
printf("Open socket error\n");
return -1;
}
// get ifr_flags
strcpy(ifr.ifr_name, net_name);
if(ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0)
{
printf("SIOCGIFFLAGS IOCTL error, %s is not valid\n", ifr.ifr_name);
close(skfd);
return -1;
}
if(ifr.ifr_flags & IFF_RUNNING)
{
// 先判斷running再判斷up還是down
// 因爲即使拔掉網線, IFF_UP依然是up
if(ifr.ifr_flags & IFF_UP)
{
printf("%s: is up\n", ifr.ifr_name);
}
printf("%s: is running\n", ifr.ifr_name);
}
else
{ // 如果是not running, 那就直接判斷爲down
printf("%s: is down\n", ifr.ifr_name);
printf("%s: is NOT running\n", ifr.ifr_name);
}
// get HW address
if(ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0)
{
printf("SIOCGIFHWADDR IOCTL error\n");
close(skfd);
return -1;
}
printf("%s: HW_ADDR: %02X:%02X:%02X:%02X:%02X:%02X\n",
net_name,
(unsigned char)ifr.ifr_hwaddr.sa_data[0],
(unsigned char)ifr.ifr_hwaddr.sa_data[1],
(unsigned char)ifr.ifr_hwaddr.sa_data[2],
(unsigned char)ifr.ifr_hwaddr.sa_data[3],
(unsigned char)ifr.ifr_hwaddr.sa_data[4],
(unsigned char)ifr.ifr_hwaddr.sa_data[5]);
// get IP address
if(ioctl(skfd, SIOCGIFADDR, &ifr) < 0)
{
printf("SIOCGIFADDR IOCTL error\n");
close(skfd);
return -1;
}
pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);
printf("%s: IP_ADDR: %s\n", net_name, inet_ntoa(pAddr->sin_addr));
// get broadcast address
if(ioctl(skfd, SIOCGIFBRDADDR, &ifr) < 0)
{
printf("SIOCGIFBRDADDR IOCTL error\n");
close(skfd);
return -1;
}
pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);
printf("%s: BROADCAST: %s\n", net_name, inet_ntoa(pAddr->sin_addr));
// get netmask address
if(ioctl(skfd, SIOCGIFNETMASK, &ifr) < 0)
{
printf("SIOCGIFNETMASK IOCTL error\n");
close(skfd);
return -1;
}
pAddr = (struct sockaddr_in *)&(ifr.ifr_addr);
printf("%s: NET_MASK: %s\n", net_name, inet_ntoa(pAddr->sin_addr));
// get gateway address
char gateway[16] = {0};
net_get_gateway(gateway);
printf("%s: GATEWAY: %s\n", net_name, gateway);
close(skfd);
return 0;
}
int main(int argc, char *argv[])
{
char net_name[8] = {0};
if(argc != 2)
{
printf("arg error\n");
return -1;
}
memcpy(net_name, argv[1], strlen(argv[1]));
return net_detect(net_name);
}
以下程序運行示例:./a.out eth0
顯然,以上程序要使用者用輪詢的方式去監測網線的狀態
方法二:
參考:https://blog.csdn.net/muclenerd/article/details/49950257
參考:https://blog.csdn.net/al86866365/article/details/79066227
參考:https://blog.csdn.net/gt945/article/details/45315911
cat /sys/class/net/eth0/carrier
如果carrier爲1表示connect,否則disconnect。
平時沒有狀態變化時,以下程序會每隔5秒(由select函數控制)循環一次,一旦監測到可讀,則read()函數就去讀數據,之後的程序就解析各個事件的數據
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>
#define BUFLEN 20480
int main(int argc, char *argv[])
{
int fd, retval;
char buf[BUFLEN] = {0};
int len = BUFLEN;
struct sockaddr_nl addr;
struct nlmsghdr *nh;
struct rtattr *attr;
fd_set rd_set;
struct timeval timeout;
int select_r;
// 1. 打開 NetLink Socket
// 第一個參數必須是AF_NETLINK或PF_NETLINK, 在Linux中, 它們倆實際爲一個東西, 它表示要使用netlink
// 第二個參數必須是SOCK_RAW或SOCK_DGRAM
// 第三個參數指定netlink協議類型, NETLINK_ROUTE 意爲“路由守護進程”
// 綁定該協議所創建出來的fd可以接收到來自內核的路由通知事件(如網路接口eth0上線)
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len));
// 2. 設定接收類型並綁定Socket
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
// 指定接收路由多播組消息 + IPV4消息
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
// bind()用於把一個打開的netlink socket與netlink源socket地址綁定在一起
bind(fd, (struct sockaddr*)&addr, sizeof(addr));
while(1)
{
//每次循環都要清空集合,否則不能檢測描述符變化
FD_ZERO(&rd_set);
//添加描述符
FD_SET(fd, &rd_set);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// read()函數讀不到東西會一直阻塞, 用select()函數可以達到不阻塞的效果
// select()能夠監視我們需要監視的文件描述符的變化情況——讀寫或是異常
// 此處select()監視到fd可讀之後, 返回值select_r大於0; fd不可讀, 則返回0
select_r = select(fd + 1, &rd_set, NULL, NULL, &timeout);
if(select_r < 0)
{
printf("select() return error\n");
}
else if(select_r > 0)
{
if(FD_ISSET(fd, &rd_set))
{
retval = read(fd, buf, BUFLEN);
for(nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, retval); nh = NLMSG_NEXT(nh, retval))
{
if(nh->nlmsg_type == NLMSG_DONE)
break;
else if(nh->nlmsg_type == NLMSG_ERROR)
return;
else if(nh->nlmsg_type == RTM_NEWLINK)
{
struct ifinfomsg *ifinfo;
ifinfo = NLMSG_DATA(nh);
printf("NEWLINK: %s", (ifinfo->ifi_flags & IFF_LOWER_UP) ? "up" : "down");
attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifinfo)));
len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifinfo));
for(; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
if(attr->rta_type == IFLA_IFNAME)
{
printf(" %s", (char*)RTA_DATA(attr));
break;
}
}
printf("\n");
}
else if(nh->nlmsg_type == RTM_NEWADDR || nh->nlmsg_type == RTM_DELADDR)
{
struct ifaddrmsg *ifaddr;
ifaddr = NLMSG_DATA(nh);
printf("%s:", (nh->nlmsg_type==RTM_NEWADDR)?"NEWADDR":"DELADDR");
attr = (struct rtattr*)(((char*)nh) + NLMSG_SPACE(sizeof(*ifaddr)));
len = nh->nlmsg_len - NLMSG_SPACE(sizeof(*ifaddr));
for(; RTA_OK(attr, len); attr = RTA_NEXT(attr, len))
{
if(attr->rta_type == IFA_ADDRESS)
{
char tmp[32] = {0};
inet_ntop(ifaddr->ifa_family, RTA_DATA(attr), tmp, sizeof(tmp));
printf(" %s", tmp);
}
if(attr->rta_type == IFA_LABEL)
{
printf(" %s", (char*)RTA_DATA(attr));
break;
}
}
printf("\n");
}
}
}
}
}
close(fd);
return 0;
}