Linux 下通過ping判斷機器有沒有外網。(不用root)

xcywt發表於2022-03-30

背景:

想實現一個判斷當前系統有沒有外網的方法,想到了兩種思路:

1)實現一個ICMP協議。但是這個需要root許可權才能執行。可以參考:https://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: https://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 ----

 

相關文章