iOS测速

前言

最近遇到一个需求(其实是闲着无聊),需要在客户端上实时显示当前的上行速度和下行速度和网络类型。目前关于iOS测速的文章和demo网上一搜一大堆,基本上使用的方法都一致,这里只是对这些方法做出总结。

我们常说的网速是什么?

为了解释这个名词,我专门搜索了我国最大的网络百科全书:百度百科。果不其然,百度百科早就收录了“网速”这个词条。并且通过了“科普中国”百科科学词条编写与应用工作项目的审核,收到了1873位网友的点赞和39次转发。

网速词条

“网速一般是指电脑或手机上网时,上传和下载数据时,请求和返回数据所用的时间长短。”这句话可以分开两部分理解。上传数据时,请求所用的时间长短,和下载数据时,返回数据所用的时间长短。这其中,数据量和时间的比值,就是网速。


计算公式

比如下载或上传一个10KB的文件,需要1秒,则网速为10KB/S

题外话:作为苹果用户,非常羡慕安卓系统能在状态栏上显示实时的上下载速度。而iOS系统,只能用一种曲线救国的方式:weight。什么?你不知道weight?拿起你的手机,使劲向左滑,这个界面上的小框框就是weight。


weight

图中的SYS Pro这个应用,就能在weight中实时显示网速。

如何测速

方法一

有了公式,我们就能很很轻松的计算出当前网速。

公式中有两个变量,“数据量”和“时间”。固定某一变量,统计另一变量的变化量。如固定时间,统计在单位时间内(假设为1秒),最大的下载数据量,就能得到当前下行速度。而固定数据量,统计下载时间,也能算出下行速度,但是需要注意的是,这时候算出的值为平均速度,并不是单位时间内的下载速度。

这种方式的优点自然是算出来的值比较准确。而缺点是需要消耗一定的流量,在当时5元30M的年代(现在依旧是5元30M),用户估计要怒删应用了。
还有一个缺点是一个TCP连接可能无法充分利用当前网络,需要多个链接一起榨干网络。

方法二

这个需求需要实时的显示网速,所以方法一不太行得通。那如何获取当前实时的网速呢?那就要祭出Uinx结构体:ifaddrs这个结构体在头文件ifaddrs.h中定义,能获取所有网卡的数据。
详细如下

struct ifaddrs {
    struct ifaddrs  *ifa_next;     /* Next item in list */
    char            *ifa_name;     /* Name of interface 端口名称*/
    unsigned int     ifa_flags;    /* Flags from SIOCGIFFLAGS 接口标志*/
    struct sockaddr *ifa_addr;     /* Address of interface 本机IP*/
    struct sockaddr *ifa_netmask;  /* Netmask of interface 子网掩码*/
    struct sockaddr *ifa_dstaddr;  /* Point-to-point destination address 对端地址*/
    void            *ifa_data;     /* Address-specific data 接口信息数据*/
};

这个结构体的详细描述可以查看这个网页:ifaddrs

其中的ifa_data中的ifi_ibytesifi_obytes就是我们需要的数据。

/*
 * Structure describing information about an interface
 * which may be of interest to management entities.
 */
struct if_data {
    /* generic interface information */
    u_char      ifi_type;       /* ethernet, tokenring, etc */
    u_char      ifi_typelen;    /* Length of frame type id */
    u_char      ifi_physical;   /* e.g., AUI, Thinnet, 10base-T, etc */
    u_char      ifi_addrlen;    /* media address length */
    u_char      ifi_hdrlen;     /* media header length */
    u_char      ifi_recvquota;  /* polling quota for receive intrs */
    u_char      ifi_xmitquota;  /* polling quota for xmit intrs */
    u_char      ifi_unused1;    /* for future use */
    u_int32_t   ifi_mtu;        /* maximum transmission unit */
    u_int32_t   ifi_metric;     /* routing metric (external only) */
    u_int32_t   ifi_baudrate;   /* linespeed */
    /* volatile statistics */
    u_int32_t   ifi_ipackets;   /* packets received on interface */
    u_int32_t   ifi_ierrors;    /* input errors on interface */
    u_int32_t   ifi_opackets;   /* packets sent on interface */
    u_int32_t   ifi_oerrors;    /* output errors on interface */
    u_int32_t   ifi_collisions; /* collisions on csma interfaces */
    u_int32_t   ifi_ibytes;     /* total number of octets received */
    u_int32_t   ifi_obytes;     /* total number of octets sent */
    u_int32_t   ifi_imcasts;    /* packets received via multicast */
    u_int32_t   ifi_omcasts;    /* packets sent via multicast */
    u_int32_t   ifi_iqdrops;    /* dropped on input, this interface */
    u_int32_t   ifi_noproto;    /* destined for unsupported protocol */
    u_int32_t   ifi_recvtiming; /* usec spent receiving when timing */
    u_int32_t   ifi_xmittiming; /* usec spent xmitting when timing */
    struct IF_DATA_TIMEVAL ifi_lastchange;  /* time of last administrative change */
    u_int32_t   ifi_unused2;    /* used to be the default_proto */
    u_int32_t   ifi_hwassist;   /* HW offload capabilities */
    u_int32_t   ifi_reserved1;  /* for future use */
    u_int32_t   ifi_reserved2;  /* for future use */
};

只要读取上一秒网卡中的流量使用情况,再读取当前的流量使用情况,就可以计算出网速。

针对第二种情况,用OC写了一个简单的Demo,放在:https://github.com/OYQ/OYTool/tree/master/OYNetSpeedTool-Objc

  • 需要注意的是,demo中并不是每一秒计算一次网速,而是每两秒计算一次,因为日常使用中并不需要精准的计算,并且大量的计算会消耗性能。

参考资料

iOS如何进行网络测速-Joy__
iOS 网速测试-godBlessMe__
IOS网速测试-角灯的技术博客

你可能感兴趣的:(iOS测速)