【Linux环境编程】获取网卡的实时网速

在windows下面,我们可以看到360或者是qq安全卫士的“安全球”,上面显示实时的网速情况。那么在linux里面如何获取网卡的实时网速?其实原理很简单,读取需要获取网速的网卡在某段时间dT内流量的变化dL,那么实时网速就出来了,Speed = dL / dt。

linux在ifaddrs.h中提供了函数:

/* Create a linked list of `struct ifaddrs' structures, one for each
   network interface on the host machine.  If successful, store the
   list in *IFAP and return 0.  On errors, return -1 and set `errno'.

   The storage returned in *IFAP is allocated dynamically and can
   only be properly freed by passing it to `freeifaddrs'.  */
extern int getifaddrs (struct ifaddrs **__ifap) __THROW;

/* Reclaim the storage allocated by a previous `getifaddrs' call.  */
extern void freeifaddrs (struct ifaddrs *__ifa)  __THROW;

系统会创建一个包含本机所有网卡信息链表,然后我们就可以在这个链表里面获取我们想要的信息。

/* The `getifaddrs' function generates a linked list of these structures.
   Each element of the list describes one network interface.  */
struct ifaddrs
{
  struct ifaddrs *ifa_next; /* Pointer to the next structure.  */

  char *ifa_name;       /* Name of this network interface.  */
  unsigned int ifa_flags;   /* Flags as from SIOCGIFFLAGS ioctl.  */

  struct sockaddr *ifa_addr;    /* Network address of this interface.  */
  struct sockaddr *ifa_netmask; /* Netmask of this interface.  */
  union
  {
    /* At most one of the following two is valid.  If the IFF_BROADCAST
       bit is set in `ifa_flags', then `ifa_broadaddr' is valid.  If the
       IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
       It is never the case that both these bits are set at once.  */
    struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */
    struct sockaddr *ifu_dstaddr; /* Point-to-point destination address.  */
  } ifa_ifu;
  /* These very same macros are defined by <net/if.h> for `struct ifaddr'.
     So if they are defined already, the existing definitions will be fine.  */
# ifndef ifa_broadaddr
#  define ifa_broadaddr ifa_ifu.ifu_broadaddr
# endif
# ifndef ifa_dstaddr
#  define ifa_dstaddr   ifa_ifu.ifu_dstaddr
# endif 

  void *ifa_data;       /* Address-specific data (may be unused).  */
};

另外这个链表我们是可以提前用ioctl来筛选的,可以通过ifa_name和ifa_flags来确定ifa_ifu里面到底选用那个union。不过这次我们是来测量实时网速的,不必要关心这个。

我们需要关心的是ifa_data这个项,关于这个项我百度了很多,一直没有发现他到底应该属于哪个结构体的。

后来无意在http://blog.chinaunix.net/uid-22832715-id-292763.html发现有类似的,但是我找不到头文件在那,所以后来干脆我直接把他放到我的头文件里面;

struct if_data{
	/* generic interface information */
	u_char	ifi_type;	/* ethernet, tokenring, etc */
	u_char	ifi_addrlen;	/* media address length */
	u_char	ifi_hdrlen;	/* media header length */
	u_long	ifi_mtu;	/* maximum transmission unit */
	u_long	ifi_metric;	/* routing metric (external only) */
	u_long	ifi_baudrate;	/* linespeed */
	/* volatile statistics */
	u_long	ifi_ipackets;	/* packets received on interface */
	u_long	ifi_ierrors;	/* input errors on interface */
	u_long	ifi_opackets;	/* packets sent on interface */
	u_long	ifi_oerrors;	/* output errors on interface */
	u_long	ifi_collisions;	/* collisions on csma interfaces */
	u_long	ifi_ibytes;	/* total number of octets received */
	u_long	ifi_obytes;	/* total number of octets sent */
	u_long	ifi_imcasts;	/* packets received via multicast */
	u_long	ifi_omcasts;	/* packets sent via multicast */
	u_long	ifi_iqdrops;	/* dropped on input, this interface */
	u_long	ifi_noproto;	/* destined for unsupported protocol */
	struct	timeval ifi_lastchange;/* last updated */
};

刚刚开始我就打印了ifi_iobytes,ifi_obytes这两个项,不管我怎么下载和上次文件,这两个量都是0。纠结了我半天,我就直接把所有变量都打印出来,发现ifi_mtu,ifi_metric,ifi_baudrate跟ifconfig eth0输出的数据很像。

[15:12 @ ~/program/netspeed]$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:22:15:67:F8:16
          inet addr:210.42.158.204  Bcast:210.42.158.255  Mask:255.255.255.0
          inet6 addr: fe80::222:15ff:fe67:f816/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:917978 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1132894 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:132866544 (126.7 MiB)  TX bytes:1250785627 (1.1 GiB)
          Interrupt:29 Base address:0x4000 

慢慢的我知道了规律,struct ifaddrs里面的ifa_data前四个字(32位)以此是发送数据包数,接收数据包数,发送字节数,接收字节数。

我就重新调整了struct if_data的结构体,由于后面的数据全为0,我就保留了4项:

struct if_data
{
    /*  generic interface information */
    u_long ifi_opackets;    /*  packets sent on interface */
    u_long ifi_ipackets;    /*  packets received on interface */
    u_long ifi_obytes;      /*  total number of octets sent */
    u_long ifi_ibytes;      /*  total number of octets received */
};  

测试OK。

[15:17 @ ~/program/netspeed]$ ./netspeed
Get eth0 Speed                  [OK]
eth0: Up Speed: 1.671066 MB/s || Down Speed: 0.036335 MB/s 

附上我已经封装好的代码:

int get_if_dbytes(struct if_info* ndev)
{
    assert(ndev);

    struct ifaddrs *ifa_list = NULL;
    struct ifaddrs *ifa = NULL;
    struct if_data *ifd = NULL;
    int     ret = 0;

    ret = getifaddrs(&ifa_list);
    if(ret < 0) {
        perror("Get Interface Address Fail:");
        goto end;
    }   

    for(ifa=ifa_list; ifa; ifa=ifa->ifa_next){
        if(!(ifa->ifa_flags & IFF_UP) && !(ifa->ifa_flags & IFF_RUNNING))
            continue;

        if(ifa->ifa_data == 0)
            continue;

        ret = strcmp(ifa->ifa_name,ndev->ifi_name);
        if(ret == 0){
           ifd = (struct if_data *)ifa->ifa_data;

           ndev->ifi_ibytes = ifd->ifi_ibytes;
           ndev->ifi_obytes = ifd->ifi_obytes;
           break;
        }
    }

    freeifaddrs(ifa_list);
end:
    return (ret ? -1 : 0);
}

int get_if_speed(struct if_speed *ndev)
{
    assert(ndev); 

    struct if_info *p1=NULL,*p2=NULL;

    p1 = (struct if_info *)malloc(sizeof(struct if_info));
    p2 = (struct if_info *)malloc(sizeof(struct if_info));
    bzero(p1,sizeof(struct if_info));
    bzero(p2,sizeof(struct if_info));

    strncpy(p1->ifi_name,ndev->ifs_name,strlen(ndev->ifs_name));
    strncpy(p2->ifi_name,ndev->ifs_name,strlen(ndev->ifs_name));

    int ret = 0;
    ret = get_if_dbytes(p1);
    if(ret < 0)     goto end;
    usleep(ndev->ifs_us);
    ret = get_if_dbytes(p2);
    if(ret < 0)     goto end;

    ndev->ifs_ispeed = p2->ifi_ibytes - p1->ifi_ibytes;
    ndev->ifs_ospeed = p2->ifi_obytes - p1->ifi_obytes;

end:
    free(p1);
    free(p2);

    return 0;
}

头文件:

#ifndef __TSPEED_H__
#define __TSPEED_H__

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <error.h>

    /* For "open" function */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct if_data
{
    /*  generic interface information */
    u_long ifi_opackets;    /*  packets sent on interface */
    u_long ifi_ipackets;    /*  packets received on interface */
    u_long ifi_obytes;      /*  total number of octets sent */
    u_long ifi_ibytes;      /*  total number of octets received */
};

struct if_info
{
    char ifi_name[16];
    unsigned long ifi_ibytes;
    unsigned long ifi_obytes;
};
struct if_speed
{
    char ifs_name[16];
    unsigned long ifs_ispeed;
    unsigned long ifs_ospeed;
    unsigned long ifs_us;
};

extern int get_if_dbytes(struct if_info *ndev);
extern int get_if_speed(struct if_speed *ndev);

#ifdef __cplusplus
}
#endif

#endif

测试代码:

int main (int argc, char **argv)
{
    struct if_speed ndev;
    int ret = 0;

    bzero(&ndev,sizeof(ndev));
    sprintf(ndev.ifs_name,"eth0");

    ndev.ifs_us = 100000;

    printf("Get %s Speed");
    ret = get_if_speed(&ndev);
    if(ret < 0)
        printf("\t\t\t[Fail]\n");
    else
        printf("\t\t\t[OK]\n");
    float ispeed ,ospeed;
    while(1){
        ispeed = ndev.ifs_ispeed * 1.0/(ndev.ifs_us/1000 * 0.001);
        ospeed = ndev.ifs_ospeed * 1.0/(ndev.ifs_us/1000 * 0.001);

        printf("%s: Up Speed: %f MB/s || Down Speed: %f MB/s                   \r",
                ndev.ifs_name,ispeed/(1024.0*1024.0),ospeed/(1024.0*1024.0));

        get_if_speed(&ndev);
    }

    return 0;
} /* ----- End of main() ----- */

可能你有更好的获取网速的办法,求留言指点!

时间: 2024-10-13 05:34:11

【Linux环境编程】获取网卡的实时网速的相关文章

Linux环境编程之共享内存区(一):共享内存区简介

Spark生态圈,也就是BDAS(伯克利数据分析栈),是伯克利APMLab实验室精心打造的,力图在算法(Algorithms).机器(Machines).人(People)之间通过大规模集成,来展现大数据应用的一个平台,其核心引擎就是Spark,其计算基础是弹性分布式数据集,也就是RDD.通过Spark生态圈,AMPLab运用大数据.云计算.通信等各种资源,以及各种灵活的技术方案,对海量不透明的数据进行甄别并转化为有用的信息,以供人们更好的理解世界.Spark生态圈已经涉及到机器学习.数据挖掘.

Linux环境编程之共享内存区(二):Posix共享内存区

现在将共享内存区的概念扩展到将无亲缘关系进程间共享的内存区包括在内.Posix提供了两种在无亲缘关系进程间共享内存区的方法: 1.内存映射文件:由open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件.(上一节就是这种技术) 2.共享内存区对象:由shm_open打开一个Posix名字(也许是在文件系统中的一个路径名),所返回的描述符由mmap函数映射到当前进程的地址空间.(本节内容) Posix共享内存区涉及以下两个步骤要求: 1.指定一个名字参数调用shm_open

Linux环境编程之文件I/O(六):文件属性

引言: 在Linux中使用ls -l filename命令查看filename的属性时,会列出文件的9种属性,例如:ls -l /etc/fstab -rw-r--r-- 1 root root 1102 2013-10-12 02:33 /etc/fstab 从左到右分别是类型与权限.文件个数.该文件或目录的拥有者.所属的组.文件大小.创建时间.文件名 以上这些文件属性的信息,都存放在一个stat的结构体中.下面就来分析一下这个结构体. 要想查看一个文件的stat结构体,可以通过stat类函数

Linux环境编程之文件I/O(五):fcntl函数

引言: 对于一个普通的文件,我们可以想到的对它的操作有,读取文件的内容.写数据到文件中,这些都是前面提到的read.write函数的作用.除此之外,还可以获取文件的其他性质,并对这些性质进行修改,比如文件的描述符.文件描述符标记.文件状态标志等等.这些对文件性质的修改就由fcntl函数完成. 函数介绍: #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ ); 参数: fd:

Linux环境编程之文件I/O(一):文件描述符

(一) 首先,对于内核来讲,它是利用"文件描述符"来访问文件的.文件描述符一般是一个非负的整数.当我们用open打开已有的文件或者用creat创建新的文件时,都会返回一个文件描述符.有了文件描述符之后,我们就可以利用该文件描述进行文件的读写,即read.write系统调用都需要文件描述符fd(file descriptor)作为其参数.从以上描述可以看出,当我们想要用read.write等系统调用对文件进行读写等操作之前,必须用open或creat系统调用得到文件的描述符. 一般Uni

Linux环境编程之同步(四):Posix信号量

信号量是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语.有三种类型:Posix有名信号量,使用Posix IPC名字标识:Posix基于内存的信号量,存放在共享内存区中:System V信号量,在内核中维护.这三种信号量都可用于进程间或线程间的同步. 图1 由两个进程使用的一个二值信号量 图2 由两个进程使用的一个Posix有名二值信号量 图3 由一个进程内的两个线程共享的基于内存的信号量 一个进程可以在某个信号量上执行的三种操作: 1.创建一个信号量,这要求调用者指定初始值,对

Linux环境编程之同步(三):读写锁

概述 互斥锁把试图进入我们称之为临界区的所有其他线程都阻塞住.该临界区通常涉及对由这些线程共享一个或多个数据的访问或更新.读写锁在获取读写锁用于读某个数据和获取读写锁用于写直接作区别.读写锁的分配规则如下: 1.只要没有线程持有某个给定的读写锁用于写,那么任意数目的线程可以持有该读写锁用于读. 2.仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配该读写锁用于写. 即只要没有线程在修改某个给定的数据,那么任意数目的线程都可以拥有该数据的读访问权.仅当没有其他线程在读或修改某个给定的数据时

Linux环境编程之进程(四):创建新进程、执行程序和进程终止

引言: 对于每个进程,都有一个非负整数表示的唯一进程ID.虽然进程的ID是唯一的,但却是可重用的.系统中有一些专用的进程.如ID为0的进程通常是调度进程,也成交换进程或系统进程(它是内核进程).进程ID为1通常是init进程,它是一个普通的用户进程.一些与进程ID有关的函数: #include <unistd.h> pid_t getpid(void);   //返回值:调用进程的进程ID pit_t getppid(void); //返回值:调用进程的父进程ID uid_t getuid(v

[linux环境编程] 信号的基本概念与操作函数

[linux环境编程] 信号的基本概念与操作函数 一.基本的概念 1.中断的基本概念 中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序. 而在Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常). 硬中断:来自硬件设备的中断 软中断:来自其它程序的中断 2.信号的基本概念 信号是软件中断,提供了一种处理异步事件的方法,可以把他看作是进