Android 使用ping命令判断当前网络状态

发布于:2024-08-13 ⋅ 阅读:(140) ⋅ 点赞:(0)

一. 介绍

        ping命令是用来测试和诊断网络连接问题的基本命令,当然我们的终端设备(手机/平板/车机)都可以用这个命令来判断当前网络是否有流量的状态,本篇文章主要介绍Linux的ping命令,因为Android系统也是使用了Linux内核。然后使用ping命令封装一个判断当前网络是否有流量的方法。

二. 作用

使用ping命令作用:

1. 检测网络连接问题:如果我们无法访问某个网站或主机,可以使用ping命令来检测网络连接是否正常。如果ping命令能够成功收到目标主机的回复,说明网络连接正常;如果无法收到回复,可能是网络故障或目标主机不可达。

2. 测量网络延迟:通过ping命令可以测量网络延迟,即从发送ICMP(Internet Control Message Protocol)(Internet控制消息协议)请求到接收到回复所需的时间。可以使用ping命令的-c选项指定发送的ICMP请求次数,然后计算平均延迟时间。较高的延迟可能会导致网络连接缓慢,需要进一步排查网络问题。

3. 检测丢包率:ping命令还可以用于检测网络丢包率,即发送的ICMP请求在传输过程中丢失的比例。可以观察ping命令的输出结果中的丢包率字段,如果丢包率较高,可能是网络拥堵或目标主机负载过高。

三. 使用格式

ping [参数] [主机名或IP地址]

ping命令运行在命令提示符终端,用法为:“ping 参数 目标主机”。其中参数为零到多个,目标主机可以是IP或者域名。

如下图:

ping命令它会持续不断地给目标IP发送ICMP数据报,上面的截图中的每一行代表了一个从ICMP响应的信息,具体包括:

icmp_seq:ICMP 数据包序号,从1开始递增,如果中间不连续代表丢包了。
ttl:生存时间,具体指允许数据包之间通过多少个路由器或跳数,数据包每经过一个路由器,ttl会减1,当ttl归零时,这个数据包的生成周期结束,处理它的路由器会丢弃这个数据包。默认情况下,Linux系统的TTL值为64或255。
time: 这个数据报的响应时间,时间越短,代表响应速度越快。
然后是统计信息,它告诉我们,一共有12个数据报被传递,全被接收,没有数据包丢失。最后一行是:最小/最大/平均响应时间和本机硬件耗费时间。

看下所有的参数:

 ping -help
Usage: ping [-aAbBdDfhLnOqrRUvV64] [-c count] [-i interval] [-I interface]
            [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos]
            [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option]
            [-w deadline] [-W timeout] [hop1 ...] destination
Usage: ping -6 [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface]
             [-l preload] [-m mark] [-M pmtudisc_option]
             [-N nodeinfo_option] [-p pattern] [-Q tclass] [-s packetsize]
             [-S sndbuf] [-t ttl] [-T timestamp_option] [-w deadline]
             [-W timeout] destination

好了,到这里,我们来介绍一下它的参数:

参数 详解
-a Audible ping. 
-A 自适应ping,根据ping包往返时间确定ping的速度;
-c count ping指定次数后停止ping;
-i interval 设定间隔几秒发送一个ping包,默认一秒ping一次;
-I interface 指定网卡接口、或指定的本机地址送出数据包;
-l preload 设置在送出要求信息之前,先行发出的数据包;
-q 不显示任何传送封包的信息,只显示最后的结果
-Q tos 设置Qos(Quality of Service),它是ICMP数据报相关位;可以是十进制或十六进制数,详见rfc1349和rfc2474文档;
-R 记录ping的路由过程(IPv4 only);
注意:由于IP头的限制,最多只能记录9个路由,其他会被忽略;
-s packetsize 指定每次ping发送的数据字节数,默认为“56字节”+“28字节”的ICMP头,一共是84字节;
包头+内容不能大于65535,所以最大值为65507(linux:65507, windows:65500);
-t ttl 设置TTL(Time To Live)为指定的值。该字段指定IP包被路由器丢弃之前允许通过的最大网段数;
-T timestamp_option

设置IP timestamp选项,可以是下面的任何一个:
'tsonly' (only timestamps)
'tsandaddr' (timestamps and addresses)
'tsprespec host1 [host2 [host3]]' (timestamp prespecified hops).

-v 使ping处于verbose方式,它要ping命令除了打印ECHO-RESPONSE数据包之外,还打印其它所有返回的ICMP数据包;
-W timeout 以毫秒为单位设置ping的超时时间;
-w deadline deadline;  单位 秒

其实常用的就是如下几个:

ping常用命令选项:

-c count:指定发送的ICMP请求次数,默认为无限次。

-i interval:指定发送ICMP请求的时间间隔,默认为1秒。

-s packetsize:指定发送的ICMP请求的数据包大小,默认为56字节。

-W timeout:指定等待ICMP回复的超时时间,默认为10秒。

-w 10   : 最长超时时间为10秒

四. 实际使用

使用ping命令访问阿里公共DNS,来判断当前网络是否有流量,间接的判断当前网络是否可用,目前国内公共的DNS有:

服务商 公共DNS服务器IP 公共DNS服务器IP
阿里DNS 223.5.5.5 223.6.6.6
114公共DNS 114.114.114.114
百度DNS 180.76.76.76
腾讯DNS 119.29.29.29
电信 101.226.4.6
联通 123.125.81.6
移动 101.226.4.6
谷歌 8.8.8.8 8.8.4.4
IBM Quad9DNS 9.9.9.9
微软DNS 4.2.2.1
华为DNS 139.9.23.90 122.112.208.1
114.115.192.11 116.205.5.1

代码如下:

 public boolean isNetworkHasTraffic() {
        Runtime runtime = Runtime.getRuntime();
        java.lang.Process ipProcess = null;
        InputStream inputStrem = null;
        BufferedReader in = null;
        try {
            ipProcess = runtime.exec("ping -c 4 -i 0.2 -w 1 223.5.5.5");
            inputStrem = ipProcess.getInputStream();
            in = new BufferedReader(new InputStreamReader(inputStrem));
            StringBuffer stringBuffer = new StringBuffer();
            String content = "";

            while ((content = in.readLine()) != null) {
                stringBuffer.append(content);
            }

            int exitValue = ipProcess.waitFor();
            Log.i(TAG, "return result after executing the ping command: " + exitValue);

            if (exitValue == 0) {
                //网络正常且有流量
                return true;
            } else if (exitValue == 1) {
                if (stringBuffer.indexOf("100% packet loss") != -1) {
                    //状态值返回1 网络丢包严重,判断为网络未连接
                    return false;
                } else {
                    return true;
                }
            } else if (exitValue == 2) {
                // 状态值返回2  网络未连接不可用
                return false;
            } else {
                // 其他异常场景
                return false;
            }
        } catch(IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (ipProcess != null) {
                ipProcess.destroy();
            }

             try {
                if (in != null) {
                    in.close();
                }
                if (inputStrem != null) {
                    inputStrem.close();
                }
            } catch (IOException e) {
                Log.e(TAG, " IOException " + e);
            }

            runtime.gc();
        }
        return false;
    }

该方法中的ping 命令:

ping -c 4 -i 0.2 -w 1 223.5.5.5

发送4次数据, 每次间隔200毫秒,deadline 为1秒执行完成   

233.5.5.5 为阿里DNS

ping -c 4 -i 0.2 -w 1 223.5.5.5
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=114 time=87.7 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=114 time=71.0 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=114 time=72.2 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=114 time=71.0 ms

--- 223.5.5.5 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 601ms
rtt min/avg/max/mdev = 71.007/75.506/87.732/7.084 ms

返回值是通过调用 Process.waitFor() 方法,根据实际测试用例场景

1. 打开和关闭 手机流量开关  当正常上网时,返回值为0   关闭流量开关: 返回值2 

2. 连接上有流量的Wifi热点,正常上网时,返回值为0

3. 连接上没有流量的Wifi热点, 无法上网,返回值为1 ,此时丢包率是100%

五. 注意事项

java中 Process的waitFor() 方法说明

JDK帮助文档上这么说:如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。但是直接调用这个方法会导致当前线程阻塞,直到退出子进程。

也就是说: 此方法不建议使用在主线程中,因为ping网络的过程是一个耗时操作,Process的waitFor()方法会阻塞当前线程 直接导致结果: 阻塞UI线程。

        经过实际测试,确实会影响主线程,验证过程: 我把这个方法写在生命周期onStart()方法中,onResume() 就会延时1秒执行。那么UI界面也会延时1秒显示,这对用户追求系统流畅角度来看,这当然是不允许的。

        当然waitFor()方法除了 0/1/2 还有很多返回值,详细见:Process.waitFor()方法的返回值

六. 方法封装

在第5小节,我们已经说明此方法不建议使用在主线程中,下面是封装的方法和使用代码

使用方法一:

new Thread(new Runnable() {
            @Override
            public void run() {
               boolean isAvalible =  isNetworkHasTraffic();
               //根据这个判断条件去处理对应的业务逻辑
               if (isAvalible) { 
                   Log.d(TAG, " 网络正常 ");
               } else {
                   Log.d(TAG, " 当前网络不可用 ");
               }
            }
        }).start();

        

使用方法二:使用AsyncTask,把耗时操作放在doInBackground方法中,在子线程中做ping操作,当结果返回时,在onPostExecute主线程中更新状态。

/*
     Params:决定了执行excute()方法时传入的参数类型,excute()方法传入的参数会传入到方法doInBackground(),所以同时也决定了doInBackground()方法内的参数类型。
     Progress:任务执行时,返回进度值的类型,即onProgressUpdate()方法内的参数类型。
     Result:任务完成后,返回的结果的类型,即doInBackground()方法的返回类型,doInBackground()方法的返回结果传入onPostExecute()方法作为参数,所以同时也决定了onPostExecute()方法的参数类型。
    *
    * */

    private abstract class getNetworkStatusTask extends AsyncTask<Void,Void,Boolean> {
        @Override
        protected Boolean doInBackground(Void... voids) {
           return isNetworkHasTraffic();
        }

        //定义为抽象方法,在子类复写
        @Override
        protected abstract void onPostExecute(Boolean aBoolean);
    }


//调用代码
   new getNetworkStatusTask(){
                @Override
                protected void onPostExecute(Boolean hastraffic) {
                    //根据判断条件处理业务逻辑代码
                    if (hastraffic) {
                        Log.d(TAG, "====网络正常=====");
                    } else {
                        Log.d(TAG, "====网络不可用=====");
                    }
                }
            }.execute();


网站公告

今日签到

点亮在社区的每一天
去签到