網絡編程難點解析(一) —— 網絡異常檢查

轉載:https://balloonwj.blog.csdn.net/article/details/78388037

一、本文目的

在涉及網絡編程的實際項目應用中,由於網絡不可能一直處於理想狀態,TCP長連接也可能隨時正常或異常地斷開,如果不予處理,那麼就可能因此而給程序帶來很多潛在的問題。 編寫該文檔的目的就在於針對網絡程序中可能遇到的各種問題,拿出來與大家探討一下具體問題的解決方案,同時本人將前期調研的一些解決方案列出來,拋磚引玉,希望可以集思廣益,尋求到更加合理的解決方案。

二、網絡斷開時機


1、正常網絡斷開

(1) CS 一方調用 Close

(2) CS 一方程序正常退出,如Ctrl + C 事件可以被系統檢測到,對應的套接字也將被系統標記爲已斷開。

2、異常網絡斷開

(1) CS 一方網線被撥出

(2) CS 之間路由故障,物理連接斷開

(3) CS 一方斷電或當機

(4) CS 一方無線信號不佳或無線接口被關閉

(5) CS 一方更改 IP 事件無法被系統檢測到,這個時候讀數據不會出錯,只會一直阻塞着;寫數據在緩衝區未滿之前也不會返回錯誤。若無法及時檢測到斷開事件,對某些應用場景而言可能存在致命的錯誤。如服務端維護的客戶端在線信息錯誤等。

三、網絡斷開常用檢測方法

1、處理正常網絡斷開

(1) select 捕捉可讀事件,read返回0

(2) epoll 捕捉可讀事件,read返回0

(3) 主動 read 返回錯誤

(4) 主動 write 返回錯誤

2、處理異常網絡斷開

(1) 應用層KeepAlive檢測

在應用層協議加入心跳握手機制,維護服務端跟客戶端之間的連通狀態,是最普遍最保險的辦法。 客戶端定時向服務端發送探測包,若服務端迴應則說明服務端在線,否則作離線處理;服務端也可對長期未發送探測包的客戶端作離線處理。 該方案所有系統都支持,跨平臺性好;對端跟自身出現網絡故障都能檢測到。不足之處在於它需要應用層協議支持,程序內部需要長期維護心跳握手包,相對比較繁瑣。

(2) 傳輸層KeepAlive檢測

除了應用層 KeepAlive 檢測機制外,TCP 內部也集成了 KeepAlive 機制,默認關閉,開啓它很方便。對端跟自身出現網絡故障都能檢測到。但不是所有的系統都支持,而且有些系統雖然支持但會影響到所有套接字,消耗額外的寬帶和流量,不建議使用。

//啓用心跳機制,如果您想關閉,將keepAlive置零即可  
setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive));  
//啓用心跳機制開始到首次心跳偵測包發送之間的空閒時間  
setsockopt(fd,SOL_TCP,TCP_KEEPIDLE,(void *)&start,sizeof(start));  
//兩次心跳偵測包之間的間隔時間  
setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,(void *)&interval,sizeof(interval));  
//探測次數,即將幾次探測失敗判定爲TCP斷開  
setsockopt(fd,SOL_TCP,TCP_KEEPCNT,(void *)&count,sizeof(count));  

(3) 網絡層KeepAlive檢測

Ping 命令幾乎是所有平臺的網絡連通性檢測命令,走網絡層 ICMP 協議,這裏考慮使用 popen 函數調用系統自帶的 Ping 命令來封裝網絡連通性檢測函數。它實際上也是一種網絡層的 KeepAlive 機制。

int checkConnect(char *dst, int cnt)  
{  
    FILE *stream;  
    sprintf(cmdBuf, "ping %s -c %d -i 0.2 | grep time= | wc -l", dst, cnt);  
    stream = popen(cmdBuf, "r");  
    fread(recvBuf, sizeof(char), sizeof(recvBuf)-1, stream);  
    pclose(stream);  
    if (atoi(recvBuf) > 0) return 0;  
    return -1;  
}  
dst 指定要檢測的目的地址,cnt 指定 Ping 嘗試的次數,-i 參數指定 Ping 嘗試的超時時間。
(4) 應用層監控內核消息機制
Netlink 是一種特殊的套接字,爲2.6.14及更高版本的Linux所特有,通過它,應用層程序可以方便地向內核訂製指定消息,如網卡上下線。也可以設置或查詢配置,如IP、路由、網絡流量信息等。
a、創建一個 netlink 套接字:

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);  

b、綁定路由多播組,監控網卡信息:

addr.nl_family = AF_NETLINK;  
   addr.nl_groups = RTNLGRP_LINK; //指定接收路由多播組消息  
   bind(fd, (struct sockaddr*)&addr, sizeof(addr)); 


c、監聽套接字,一旦可讀,解析其內容,實時監控網卡上下線事件。

優點:實時性高,使用方便。

缺點:跨平臺性不佳,只能檢測自身網絡故障。

(5) 應用層網卡信息輪詢機制

網卡信息輪詢機制就是定期調用 ioctl 函數執行如下操作:

struct ifconf ifc;  
   struct ifreq ifrcopy;  
   //獲取網卡信息列表  
   ioctl(fd, SIOCGIFCONF, (char *)&ifc);  
   //獲取網卡上下線狀態  
   ioctl(fd, SIOCGIFFLAGS, &ifrcopy);  
   //獲取 MAC 地址  
   ioctl(fd, SIOCGIFHWADDR, (char *)(&ifrcopy);  
   //獲取 IP 地址  
   ioctl(fd, SIOCGIFADDR, (char *)&ifrcopy);  
   //獲取廣播地址  
   ioctl(fd, SIOCGIFBRDADDR, &ifrcopy)); 

缺點:

a、跨平臺性不佳。

可以成功移植到 Linux、Android、Windows平臺,但由於 iPhone 平臺上獲取MAC跟IP的參數不同,需特殊處理。

b、實時性跟靈活性不高。

c、耗費資源,影響性能。

d、只能檢測自身網絡故障。


文章來源:

http://www.cpplive.com/html/1670.html
————————————————
版權聲明:本文爲CSDN博主「analogous_love」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://balloonwj.blog.csdn.net/article/details/78388037

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