方法一:
参考:https://blog.csdn.net/hbk320/article/details/47300067
由于linux下的ifconfig命令就能够实现在应用层监控网线插拔状态,例如当网线连接正常时,使用ifconfig eth0命令,打印的信息中会有RUNNING,而拔掉网线后,再使用ifconfig eth0命令,RUNNING就不见了。所以,实现Linux应用层监控网线插入状态就相当于自己写一个ifconfig函数。
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}