项目中需要提供主机当前网络流量的信息,主要是占用的当前的网络的带宽,提供给用户以作决策。如何获得网络流量信息呢?
思路有两个:
1、libpcap,捕包进行计算
2、/proc/net/dev文件中获得
方法一,很dirty,而且需要引入libpcap库,效率不好,而且libpcap还可能丢报,统计的信息不一定准确。
方法二,/proc/net/dev文件中没有speed的信息,只有recv和trans的总量,这是内核提供的,怎么也是准确的,没有speed信息,只好自己动手丰衣足食了;-)
忽 然想起一些applet有监控网络的功能,会提供传输速度的信息,例如Gnome System Monitor就可以,但是太大了,不好裁减。有一些比较小的applet,例如gkrellm和conky,都是一个类型的。翻看了两个代 码,gkrellm是GTK的,conky是纯C的,当然选择conky了,search了一下,裁减了conky的一些数据结构和提取了部分代码,写了 提供网络发送和接收速度信息的函数:
#ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include <time.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/in.h> #define MAX_BUFF_SIZE 256 struct net_stat { const char *dev; int up; long long last_read_recv, last_read_trans; //last read total num long long recv, trans; //real total num double recv_speed, trans_speed; }; /* network interface stuff */ static struct net_stat netstats[16]={0}; struct net_stat *get_net_stat(const char *dev) { unsigned int i=0; if (!dev) { return 0; } /* find interface stat */ for (i = 0; i < 16; i++) { if (netstats[i].dev && strcmp(netstats[i].dev, dev) == 0) { return &netstats[i]; } } /* wasn't found? add it */ if (i == 16) { for (i = 0; i < 16; i++) { if (netstats[i].dev == 0) { netstats[i].dev = strndup(dev, MAX_BUFF_SIZE); return &netstats[i]; } } } fprintf(stderr, "too many interfaces used (limit is 16)"); return 0; } void clear_net_stats(void) { memset(netstats, 0, sizeof(netstats)); } void update_net_stats(const char* dev, double delta) { FILE *net_dev_fp; // FIXME: arbitrary size chosen to keep code simple. int i=0; char buf[256]={0}; /* open file and ignore first two lines */ if (!(net_dev_fp = fopen("/proc/net/dev", "r"))) { fprintf(stderr, "fopen failed./n"); clear_net_stats(); return; } fgets(buf, 255, net_dev_fp); /* garbage */ fgets(buf, 255, net_dev_fp); /* garbage (field names) */ /* read each interface */ for (i = 0; i < 16; i++) { struct net_stat *ns=NULL; unsigned char *s=NULL, *p=NULL; unsigned long long r=0, t=0; unsigned long long last_recv=0, last_trans=0; if (fgets(buf, 255, net_dev_fp) == NULL) { //File EOF break; } //Skip Space p = buf; while (isspace((int) *p)) { p++; } /*s: network interface name*/ s = p; //Skip Network Interface Name while (*p && *p != ':') { p++; } if (*p == '/0') { continue; } *p = '/0'; /*p: reveive bytes*/ p++; //Judge Network Interface or Not? if(strcmp(s, dev) != 0) continue; //Get struct net_stat ns = get_net_stat(s); ns->up = 1; last_recv = ns->recv; last_trans = ns->trans; /* bytes packets errs drop fifo frame compressed multicast|bytes ... */ sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld", &r, &t); /* if recv or trans is less than last time, an overflow happened */ if (r < ns->last_read_recv) { last_recv = 0; } else { ns->recv += (r - ns->last_read_recv); } ns->last_read_recv = r; if (t < ns->last_read_trans) { last_trans = 0; } else { ns->trans += (t - ns->last_read_trans); } ns->last_read_trans = t; /* calculate speeds */ if(last_recv == 0) ns->recv_speed = 0; else ns->recv_speed = (ns->recv - last_recv) / delta; if(last_trans == 0) ns->trans_speed = 0; else ns->trans_speed = (ns->trans - last_trans) / delta; /* //First Time Run, Or Time Overflow if(current_time == 0) { ns->recv_speed = 0; ns->trans_speed = 0; } */ //Find Network Interface, And Work Over break; } fclose(net_dev_fp); } int main() { int i=0; unsigned int sleep_time = 0; struct net_stat* ns = NULL; time_t current_time=0, last_time=0, delta_time=0; srand(time(NULL)); for(i=0; i<10000; i++) { last_time = current_time; current_time = time(NULL); delta_time = current_time-last_time; printf("Delta Time: %u Seconds/n", delta_time); update_net_stats("eth0", delta_time); ns = get_net_stat("eth0"); printf("Recv Speed: %f KB/s/n", ns->recv_speed/1024); printf("Send Speed: %f KB/s/n", ns->trans_speed/1024); sleep_time = 1+random()%5; printf("Sleep %d Second.../n", sleep_time); sleep(sleep_time); } return 0; } |