Linux应用层如何监测网线的插拔状态

方法一:

参考: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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章