Linux 下通过ping判断机器有没有外网。(不用root)

背景:

想实现一个判断当前系统有没有外网的方法,想到了两种思路:

1)实现一个ICMP协议。但是这个需要root权限才能运行。可以参考://www.cnblogs.com/xcywt/p/16070814.html

2)通过ping指令,解析ping的结果来判断有没有网。

 

代码:

0)命令:system(“ping 192.168.1.21 -c 2 > PingTempTest.txt”);   //   -c 2 表示ping两次。

1)再去解析PingTempTest.txt中的内容。

3)实际使用时由于ping是耗时操作,为了不阻塞主线程,开了一个子线程去调用ping。

4)注意:如果系统是中文版本,可能ping运行的结果格式不一致。将无法使用下面的解析方法。

5)编译时需要指定C++14,和链接线程库。

头文件:

/*
 * @author:xcywt
 * @date:2022-03-30
 * @contact me: //www.cnblogs.com/xcywt/
 */

#ifndef __DDR_CHECKFOREXTRANET_H__
#define __DDR_CHECKFOREXTRANET_H__

#include <string>

namespace DDRSys
{
    /*
        检测有没有外网的。原理就是:尝试ping某个ip,然后解析ping的结果。
        每次创建一个对象会开辟一个线程。在线程中循环ping。
        可以通过 GetNetState 取得结果。

        测试系统:ubuntu16.04
        用法: 
        void test_20220330()
        {
            DDRSys::CheckForExtranet check;
            check.SetPingIntervals(10);
            //check.SetPingIp("114.114.114.114");
            //check.SetPingIp("114.114.114.11");
            //check.SetPingIp("8.8.8.8");
            //check.SetPingIp("8.8.8.7");
            check.SetPingIp("192.168.1.21");
            int nnn = 0;
            while (1)
            {
                nnn++;
                //printf("test_20220330() nnn:%d State:%d\n", nnn, check.GetNetState());
                std::this_thread::sleep_for(std::chrono::milliseconds(2000));
                if (nnn > 100)
                    break;
            }
        }

        编译时需要指定C++14,和链接线程库。
        g++ main.cpp CheckForExtranet.cpp -std=c++14  -lpthread
    */
    
    class CheckForExtranet
    {
    public:
        CheckForExtranet();

        ~CheckForExtranet();

        // 设置需要ping的ip,一般指定 8.8.8.8 或者 114.114.114.114
        void SetPingIp(std::string ip);

        // 设置间隔时间。单位为秒。设置为10,表示每隔10秒尝试ping一次
        void SetPingIntervals(int sec);

        // -1:表示无效值  0:没网  1:有网
        int GetNetState();

    private:
        class IMPL;
        IMPL *m_pImp = nullptr;
    };
}

#endif // __DDR_CHECKFOREXTRANET_H__

源文件:

#include "CheckForExtranet.h"
#include <thread>
#include <mutex>
#include <fstream>
#include <vector>

namespace DDRSys
{
    std::vector<std::string> split(const std::string &text, char sep)
    {
        std::vector<std::string> tokens;
        std::size_t start = 0, end = 0;
        while ((end = text.find(sep, start)) != std::string::npos) {
            if (end != start) {
                tokens.emplace_back(text.substr(start, end - start));
            }
            start = end + 1;
        }
        if (end != start) {
            tokens.emplace_back(text.substr(start));
        }
        return tokens;
    }

    class CheckForExtranet::IMPL
    {
    public:
        IMPL() 
        {
            m_subThread = std::thread(subThread, (void*)this);
        }

        ~IMPL()
        {
            printf("[%s] CheckForExtranet::IMPL::~IMPL() +++ \n", GetLogPrev().c_str());

            if (1)
            {
                std::lock_guard<std::mutex> lll(m_mutex);
                m_bQuit = true;
            }
            if (m_subThread.joinable())
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(2000));
                m_subThread.join();
            }

            printf("[%s] CheckForExtranet::IMPL::~IMPL() --- \n", GetLogPrev().c_str());
        }

        std::string GetLogPrev()
        {
            return "ExtranetLog";
            //return DDRSys::GetCurTimeStamp_MilSec(); // 这个函数返回当时时间戳的
        }

        void SetPingIp(std::string ip)
        {
            std::lock_guard<std::mutex> lll(m_mutex);
            m_strPingIp = ip;
        }

        void SetPingIntervals(int sec)
        {
            std::lock_guard<std::mutex> lll(m_mutex);
            m_Intervals = sec;
        }

        int GetNetState()
        {
            std::lock_guard<std::mutex> lll(m_mutex);
            auto curr = m_nState;
            return curr;
        }

        void _myLoop()
        {
            printf("[%s] CheckForExtranet::IMPL::_myLoop() +++ \n", GetLogPrev().c_str());

            const int waitTime = 500;
            int waitCount = -1;
            while (1)
            {
                int Intervals = 0;
                bool bQuit = false;
                if (1)
                {
                    std::lock_guard<std::mutex> lll(m_mutex);
                    bQuit = m_bQuit;
                    Intervals = m_Intervals;
                }

                if (bQuit)
                {
                    break;
                }

                std::this_thread::sleep_for(std::chrono::milliseconds(waitTime));
                if ((waitCount > (m_Intervals * 1000) / waitTime) || (waitCount < 0))
                {
                    waitCount = 0;
                    Ping();
                }
                waitCount++;
            }
            printf("[%s] CheckForExtranet::IMPL::_myLoop() --- \n", GetLogPrev().c_str());
        }

        static void subThread(void *param)
        {
            if (param)
            {
                auto *pThis = (CheckForExtranet::IMPL*)param;
                pThis->_myLoop();
            }
        }

        void Ping()
        {            
            std::string strip = "";
            if (1)
            {
                std::lock_guard<std::mutex> lll(m_mutex);
                strip = m_strPingIp;
            }

            std::string fileNameTemp("PingTempTest");
            fileNameTemp += ".txt";

            std::string cmd("ping ");
            cmd += strip;
            cmd += " -c 2 > ";
            cmd += fileNameTemp;
            printf("[%s] Start ping:%s cmd:[%s] ++++ \n", GetLogPrev().c_str(),strip.c_str(), cmd.c_str());
            system(cmd.c_str());

            // cmd eg:ping 192.168.1.21 -c 2 > PingTempTest.txt
            // 这里是解析上面的结果。如果系统是中文版本,可能ping运行的结果格式不一致。将无法使用下面的解析方法。
            int state = -1;
            std::vector<std::string> vecTTT;
            std::ifstream in(fileNameTemp.c_str());
            if (in.is_open())
            {
                std::string s;
                while (getline(in, s))
                {
                    vecTTT.push_back(s);
                }
                in.close();
            }

            for (auto item : vecTTT)
            {
                if ((int)item.find("packet loss") > 0)
                {
                    // 2 packets transmitted, 0 received, 100% packet loss, time 1001ms
                    auto vec = split(item, ','); // 这个函数是分隔用的。根据逗号分隔,结果放在vec中。
                    for (auto ii : vec)
                    {
                        if ((int)ii.find("packet loss") > 0)
                        {
                            int index = ii.find("%");
                            std::string str;
                            str.assign(ii.begin(), ii.begin() + index);
                            int packetLoss = std::atoi(str.c_str());
                            if (packetLoss >= 80)
                                state = 0; // 丢失率大于80%就认为是没网了
                            else
                                state = 1;
                            printf("[%s] ReadInfo:%s packetLoss:%d state:%d\n", GetLogPrev().c_str(), ii.c_str(), packetLoss, state);
                            break;
                        }
                    }
                    break;
                }
            }

            /*
            dadao@dadao:~$ ping 8.8.8.8 -c 2
            PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
            64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=48.8 ms
            64 bytes from 8.8.8.8: icmp_seq=2 ttl=116 time=50.3 ms

            --- 8.8.8.8 ping statistics ---
            2 packets transmitted, 2 received, 0% packet loss, time 1001ms
            rtt min/avg/max/mdev = 48.893/49.609/50.326/0.750 ms
            dadao@dadao:~$ ping 8.8.8.82 -c 2
            PING 8.8.8.82 (8.8.8.82) 56(84) bytes of data.

            --- 8.8.8.82 ping statistics ---
            2 packets transmitted, 0 received, 100% packet loss, time 1001ms

            dadao@dadao:~$
            */

            if (1)
            {
                std::lock_guard<std::mutex> lll(m_mutex);
                m_nState = state;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));            
            printf("[%s] End ping:%s state:%d ---- \n", GetLogPrev().c_str(), strip.c_str(), state);
        }

    private:
        std::thread m_subThread;
        std::mutex m_mutex;
        bool m_bQuit = false;

        int m_nState = -1; // // -1表示结果无意义   0表示没网  1表示有网
        std::string m_strPingIp = "8.8.8.8";
        int m_Intervals = 10;
    };
    
    CheckForExtranet::CheckForExtranet()
    {
        m_pImp = new CheckForExtranet::IMPL();
    }

    CheckForExtranet::~CheckForExtranet()
    {
        if (m_pImp)
        {
            m_pImp->~IMPL();
            m_pImp = nullptr;
        }
    }

    void CheckForExtranet::SetPingIp(std::string ip)
    {
        if (m_pImp)
        {
            m_pImp->SetPingIp(ip);
        }
    }

    void CheckForExtranet::SetPingIntervals(int sec)
    {
        if (m_pImp)
        {
            m_pImp->SetPingIntervals(sec);
        }
    }

    int CheckForExtranet::GetNetState()
    {
        if (m_pImp)
        {
            return m_pImp->GetNetState();
        }
        return -1;
    }

}

 

实际效果:

dadao@dadao:~/workspace/test/PIng$ ./a.out
[ExtranetLog] CheckForExtranet::IMPL::_myLoop() +++
[ExtranetLog] Start ping:192.168.1.21 cmd:[ping 192.168.1.21 -c 2 > PingTempTest.txt] ++++
[ExtranetLog] ReadInfo: 0% packet loss packetLoss:0 state:1
[ExtranetLog] End ping:192.168.1.21 state:1 —-
[ExtranetLog] Start ping:192.168.1.21 cmd:[ping 192.168.1.21 -c 2 > PingTempTest.txt] ++++
[ExtranetLog] ReadInfo: 0% packet loss packetLoss:0 state:1
[ExtranetLog] End ping:192.168.1.21 state:1 —-