背景:
想實現一個判斷當前系統有沒有外網的方法,想到了兩種思路:
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 ----