歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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://www.linuxidc.com/Linux/2014-11/109289.htm 發現有類似的,但是我找不到頭文件在那,所以後來干脆我直接把他放到我的頭文件裡面;

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() ----- */

可能你有更好的獲取網速的辦法,求留言指點!

Copyright © Linux教程網 All Rights Reserved