聲明:如需引用或者摘抄本博文源碼或者其文章的,請在顯著處注明,來源於本博文/作者,以示尊重勞動成果,助力開源精神。也歡迎大家一起探討,交流,以共同進步,乃至成為朋友~ 0.0
由於學習操作系統實驗課程的緣故,這幾日都在搗鼓底層,也發現底層確實蠻有意思的。其中,有一個實驗要求實時檢測網絡速率嘛,今晚終於搞定了~
大致思路如下:
1.讀取linux文件系統下的/proc/net/dev目錄,讀取其網絡配置信息
溫馨提示:除了第一/二行外,剩余各行分別是計算機主機的【各網卡(有線網卡*/本機回環*/無線網卡*等)】的網絡收發信息(字節/包/幀等)

2.使用相關函數讀取其網絡信息(關鍵信息如下:接收的總字節數和發送的總字節數)
3.網速的計算:(需要間隔一段(一般為1秒),然后再次執行步驟2)
(net_current_receive_total - net_previous_receive_total) + (net_current_transmit_total - net_previous_transmit_total) / 2
注意事項:
一般使用C庫函數的文件操作(常用API:fopen;fseek;fclose;fget(按行讀取);sscanf等),但我並不想說這些。
我想提倆很小的細節,但確實是坑了我好久好久的:
坑1:數據是需要先讀兩行無關的信息后,才開始讀我們需要的網絡配置信息的
坑2:獲取並重置游標位置為文件開頭處(注意:此處是大坑 原因:每次更新計算機的網絡的信息時,必須重新讀入,或者重置游標的默認位置為文件流開始位置處)
net_off = fseek(net_stream, 0, SEEK_SET);
程序共有如下函數:
//注意:所有的log相關的執行程序,僅僅是打印日志,方便讀者查看監測細節,如需實用時,除去log/lineLog相關代碼即可,不會影響程序正常執行 void net_update(bool log);//初始化【必選】 void net_init(bool log, bool lineLog);//更新網絡各參數信息【必選】 float net_usage(char * TYPE, bool log, bool lineLog);//通過TYPE值,獲取網絡信息的指定參數【必選】 void net_close();//關閉網絡監測【必選】 //測試用例執行函數 void calculate(bool initLog, bool usageLog, bool lineLog) void execute(bool initLog, bool usageLog, bool lineLog) int main()
源碼如下:(可以根據每個用戶的需要,一般地,更改define部分和去除掉log相關形參即可,實現實時網絡檢測)
/*
@url:http://www.cnblogs.com/johnnyzen/p/8009053.html
@author:Johnny Zen
@school:XiHua University
@contact:johnnyztsd@gmail.com or 1125418540@qq.com
@date:2017-12-10 01:33
@description:實現網絡檢測功能(也提供實時)
@environment:Linux For Ubuntu 16.04/64
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
//為了兼容C++
#undef bool
#undef true
#undef false
#define bool int
#define true 1
#define false 0
#define NET_INFO_PATH "/proc/net/dev"
#define NET_DIFF_TIME 1 //時間差閾值(單位:秒)
//全局參數:以供隨時調用函數,隨時返回值(存在延時1s以內)
static time_t net_previous_timeStamp;//記錄上一次系統時間戳 時間單位:秒
static time_t net_current_timeStamp;//記錄當前系統時間戳 時間單位:秒
static double net_dif_time;//記錄時間差 時間單位:秒
static char net_file[64] = {NET_INFO_PATH};//要讀取的目標文件名
static int net_off;//記錄文件游標當前偏移位置
static int line_num;//記錄行數
static FILE *net_stream; //訪問虛擬文件系統的文件流
static char net_buffer[256];//行緩沖區
static char *net_line_return;//記錄讀取每行的時候的返回結果(NULL或者返回 net_buffer)
static char net_tmp_itemName[32];//臨時存放文件中的每行的項目名稱
static int net_itemReceive;//存放每一個網卡的接受到的字節總數(單位:Byte)
static int net_itemTransmit;//存放每一個網卡的已發送的字節總數(單位:Byte)
static int net_current_receive_total;//存放【當前】收到的網絡包字節總數(單位:Byte)
static int net_previous_receive_total;//存放【上一次】收到的網絡包字節總數(單位:Byte)
static int net_receive_speed;//平均接受網絡字節速度
static int net_current_transmit_total; //存放【當前】已發送的網絡包字節總數(單位:Byte)
static int net_previous_transmit_total;//存放【上一次】已發送的網絡包字節總數(單位:Byte)
static int net_transmit_speed;//平均發送網絡字節速度
static float net_averageLoad_speed; //網絡負載情況(單位:Bytes/s)
//(net_current_receive_total - net_previous_receive_total) + (net_current_transmit_total - net_previous_transmit_total) / 2
static float net_result; //函數返回值
//聲明所有函數體
//注意:所有的log相關的執行程序,僅僅是打印日志,方便讀者查看監測細節,如需實用時,除去log/lineLog相關代碼即可,不會影響程序正常執行
void net_update(bool log);//初始化【必選】
void net_init(bool log, bool lineLog);//更新網絡各參數信息【必選】
float net_usage(char * TYPE, bool log, bool lineLog);//通過TYPE值,獲取網絡信息的指定參數【必選】
void net_close();//關閉網絡監測【必選】
//更新網絡各信息
void net_update(bool log){// log:是否按行打印日志
net_previous_receive_total = net_current_receive_total;//初始化上一次接收總數據
net_previous_transmit_total = net_current_transmit_total;//初始化上一次發送總數據
net_current_receive_total = 0; //對當前網絡接收數據總數清零,重新計算
net_current_transmit_total = 0;//對當前網絡發送數據總數清零,重新計算
//獲取網絡的初始化數據
/*step1: 獲取並重置游標位置為文件開頭處(注意:此處是大坑 原因:每次更新計算機的網絡的信息時,必須重新讀入,或者重置游標的默認位置)*/
net_off = fseek(net_stream, 0, SEEK_SET);//獲取並重置游標位置為文件流開頭處,SEEK_CUR當前讀寫位置后增加net_off個位移量,重置游標位置比重新打開並獲取文件流的資源要低的道理,應該都懂吧 0.0
line_num = 1;//重置行數
net_line_return = fgets (net_buffer, sizeof(net_buffer), net_stream);//讀取第一行
//printf("[net_update] line %d: %s\n", line_num, net_line_return);
line_num++;
net_line_return = fgets (net_buffer, sizeof(net_buffer), net_stream);//讀取第二行
//printf("[net_update] line %d: %s\n", line_num, net_line_return);
net_itemReceive = 0;
net_itemTransmit = 0;
int flag = 1;
while(flag == 1){
line_num++;
net_line_return = fgets (net_buffer, sizeof(net_buffer), net_stream);
net_itemReceive = 0;
net_itemTransmit = 0;
if(net_line_return != NULL){
sscanf( net_buffer,
"%s%d%d%d%d%d%d%d%d%d",
net_tmp_itemName,
&net_itemReceive,
&net_itemTransmit,
&net_itemTransmit,
&net_itemTransmit,
&net_itemTransmit,
&net_itemTransmit,
&net_itemTransmit,
&net_itemTransmit,
&net_itemTransmit);
net_current_receive_total += net_itemReceive;//初始化接收字節總數
net_current_transmit_total += net_itemTransmit;//初始化發送字節總數
} else {
flag = -1;
}
if(log == true){
if(flag != -1)//如果當前行不是末尾行時,不添加換行符
printf("[net_update] line %d: %s", line_num, net_line_return);
else //反之,添加換行符
printf("[net_update] line %d: %s\n", line_num, net_line_return);
printf("[net_update] line %d:net_itemReceive: %d\n", line_num, net_itemReceive);
printf("[net_update] line %d:net_itemTransmit: %d\n", line_num, net_itemTransmit);
printf("[net_update] line %d:net_current_receive_total: %d\n", line_num, net_current_receive_total);
printf("[net_update] line %d:net_current_transmit_total: %d\n\n", line_num, net_current_transmit_total);
}
}
}
//初始化
void net_init(bool log, bool lineLog){//log:打印更新的【網絡各參數信息】,以便檢測細節; lineLog:按行打印網絡文件信息細節
//讀取數據
net_line_return = "INIT"; //設置初始值,只要不為空字符串即可
/*step1: 打開文件 (注意:此處是大坑 原因:每次更新計算機的網絡的信息時,必須重新讀入,或者重置游標的默認位置)*/
net_stream = fopen (net_file, "r"); //以R讀的方式打開文件再賦給指針fd
net_off = fseek(net_stream, 0, SEEK_SET);//獲取並重置游標位置為文件流開頭處,SEEK_CUR當前讀寫位置后增加net_off個位移量
net_update(lineLog);//更新計算機的網絡的信息
net_previous_receive_total = net_current_receive_total;//初始化上一次接收總數據
net_previous_transmit_total = net_current_transmit_total;//初始化上一次發送總數據
net_receive_speed = 0;//初始化接收網速為:0
net_transmit_speed = 0;//初始化發送網速為:0
net_averageLoad_speed = 0.0; //初始化網絡負載速度為:0
net_previous_timeStamp = net_current_timeStamp = time(NULL);//開始獲取初始化的時間戳
net_dif_time = 0;//初始化時間差
if(log == true){
printf("\n[INIT NET] net_current_timeStamp: %f\n", (double)net_current_timeStamp);
printf("\n[INIT NET] Receive Total: %d bytes.\n", net_current_receive_total);
printf("\n[INIT NET] Receive Speed: %d bytes/s.\n", net_receive_speed);
printf("\n[INIT NET] Transmit Total: %d bytes.\n", net_current_transmit_total);
printf("\n[INIT NET] Transmit Speed: %d bytes/s.\n", net_transmit_speed);
printf("\n[INIT NET] Average Load Speed: %f bytes/s.\n", net_averageLoad_speed);
printf("*********************************************************************************************************\n");
}
}
//參數:TYPE:["receiveTotal/receiveSpeed/transmitTotal/transmitSpeed/averageLoadSpeed"]
float net_usage(char * TYPE, bool log, bool lineLog){//log:打印更新的【網絡各參數信息】,以便檢測細節; lineLog:按行打印網絡文件信息細節
net_current_timeStamp = time(NULL);//time() 單位:秒
printf("[net_usage] net_current_timeStamp: %f.\n", (double)net_current_timeStamp);//time_t[long int]必須轉型,否則無法正常(輸出顯示:0.000)
net_dif_time = (double)(net_current_timeStamp - net_previous_timeStamp);//計算時間差(單位:秒)
if( (net_dif_time) >= NET_DIFF_TIME ){//只有滿足達到時間戳以后,才更新接收與發送的網絡字節數據信息
net_update(lineLog);//是否按行打印更新的網絡參數信息
net_receive_speed = (net_current_receive_total - net_previous_receive_total) / NET_DIFF_TIME;//更新接收網速(單位:字節/秒)
net_transmit_speed = (net_current_transmit_total - net_previous_transmit_total) / NET_DIFF_TIME;//更新發送網速(單位:字節/秒)
net_averageLoad_speed = (net_receive_speed + net_transmit_speed) / 2; //更新平均網絡負載情況
//更新時間戳
net_previous_timeStamp = net_current_timeStamp;
}
if(log == true){
printf("\n[NET] LAST Receive Total: %d bytes [%f KB].\n", net_previous_receive_total, (double)(net_previous_receive_total / 1024));
printf("\n[NET] NOW Receive Total: %d bytes [%f KB].\n", net_current_receive_total, (double)(net_current_receive_total / 1024));
printf("\n[NET] NOW Receive Speed: %d bytes/s [%f KB/s].\n", net_receive_speed, (double)(net_receive_speed / 1024));
printf("\n[NET] LAST Transmit Total: %d bytes [%f KB].\n", net_previous_transmit_total, (double)(net_previous_transmit_total / 1024));
printf("\n[NET] NOW Transmit Total: %d bytes [%f KB].\n", net_current_transmit_total, (double)(net_current_transmit_total / 1024));
printf("\n[NET] NOW Transmit Speed: %d bytes/s [%f KB/s].\n", net_transmit_speed, (double)(net_transmit_speed / 1024));
printf("\n[NET] Average Load Speed: %f bytes/s [%f KB/s].\n", net_averageLoad_speed, (double)(net_averageLoad_speed / 1024));
printf("*********************************************************************************************************\n");
}
if(TYPE == "receiveTotal"){
net_result = (float)net_current_receive_total;
} else if(TYPE == "receiveSpeed"){
net_result = (float)net_receive_speed;
} else if(TYPE == "transmitTotal"){
net_result = (float)net_current_transmit_total;
} else if(TYPE == "transmitSpeed"){
net_result = (float)net_transmit_speed;
} else if(TYPE == "averageLoadSpeed"){
net_result = net_averageLoad_speed;
} else {// TYPE 不存在時,拋出異常值 -1
net_result = -1;
}
return net_result;
}
//關閉網絡監控
void net_close(){
fclose(net_stream); //關閉文件net_stream
}
///////////////////////////////////
// ↓demo↓ //
///////////////////////////////////
/*
initLog:是否打印【初始化后的網絡各參數】日志;
usageLog:是否打印更新的當前的【網絡各參數信息】,以便檢測細節;
lineLog:是否【按行】打印【網絡當前狀態信息細節】
*/
/*
void calculate(bool initLog, bool usageLog, bool lineLog){
printf("\n[MAIN] receiveTotal:%f\n", net_usage("receiveTotal", usageLog, lineLog));//log:打印日志在屏幕上:以便檢測細節
printf("*********************************************************************************************************\n");
sleep(3);
printf("\n[MAIN] receiveTotal:%f\n", net_usage("receiveTotal", usageLog, lineLog));//log:打印日志在屏幕上:以便檢測細節
printf("*********************************************************************************************************\n");
}
void execute(bool initLog, bool usageLog, bool lineLog){//
system("cat /proc/net/dev");
printf("*********************************************************************************************************\n");
net_init(initLog, lineLog);
calculate(initLog, usageLog, lineLog);
net_close();
}
int main(){
execute(true, true, true);//測試用例1
printf("########################################################################################################\n");
execute(false, true, false);//測試用例2
return 0;
}
*/
運行效果:
測試用例2:

測試用例1:



參考文獻:
原創。
