Android Traceroute 功能实现

经常在windows下开发网络功能的人 经常会使用的命令就是tracert 。而实际上 在app开发中,我们也经常要碰到类似的情况。比如你的app

出现了问题,你总不能让用户想办法 去tracert吧。你肯定要知道你的app 是在网络中的哪一个部分出了问题。我举个最简单的例子。国内有很多做外包的

公司 在开发过程中 需要调用 facebook 等公司提供的sdk 或者接口。当然了 我们 在天朝吗 所以我们在做类似功能的时候 一般要使用vpn来访问。

但是很多vpn的情况 也是很不稳定的,在开发过程中 能否迅速的找到网络不通的原因 是很重要的。尤其是你给欠发达地区 的客户在开发app的时候 

一旦网络连接不同,你怎么证明你的app 是运行正常的 ,是他们的网络基础不正常?

 

那我们今天就来看一下如果在android app下面实现这个tracert功能。

 

首先我们想到的肯定是 实现一下tracert这个命令的java版,但实际上 我们在网络上找不到类似的资源,自己去实现包括icmp 报文之类的又比较麻烦(日后我会自己实现一份)

所以我们想到 android 是基于linux的 为何不直接调用这个命令呢?我们取得tracert的结果不就行了吗,但实际上很多linux 版本是没有 tracert的这个命令的。

当然了 很多人会说 linux下是叫 Traceroute 这个命令的,但是很遗憾的是多数android 手机内置命令都不包含这个。。。。

经过一番思索 我决定 用ping 命令的参数  来实现 Traceroute 这个功能。 其实主要是 -t 和 -c 这2个参数了。另外就是对ttl 这个值有一定的理解 即可实现类似于

Traceroute  的功能。

代码如下:

 

  1 package com.example.traceroute;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStreamReader;
  6 import java.net.InetAddress;
  7 import java.net.UnknownHostException;
  8 import java.util.ArrayList;
  9 import java.util.List;
 10 
 11 import android.app.Activity;
 12 import android.os.AsyncTask;
 13 import android.os.Bundle;
 14 import android.text.TextUtils;
 15 import android.util.Log;
 16 import android.view.View;
 17 import android.widget.Button;
 18 import android.widget.EditText;
 19 
 20 public class MainActivity extends Activity {
 21 
 22     // 输入网址框
 23     private EditText et;
 24 
 25     // 开始traceroute的button
 26     private Button searchButton;
 27 
 28     // 最大的ttl跳转 可以自己设定
 29     private final int MAX_TTL = 30;
 30 
 31     // 都是一些字符串 用于parse 用的
 32     private static final String PING = "PING";
 33     private static final String FROM_PING = "From";
 34     private static final String SMALL_FROM_PING = "from";
 35     private static final String PARENTHESE_OPEN_PING = "(";
 36     private static final String PARENTHESE_CLOSE_PING = ")";
 37     private static final String TIME_PING = "time=";
 38     private static final String EXCEED_PING = "exceed";
 39     private static final String UNREACHABLE_PING = "100%";
 40 
 41     // 初始化默认ttl 为1
 42     private int ttl = 1;
 43     private String ipToPing;
 44     // ping耗时
 45     private float elapsedTime;
 46 
 47     // 存放结果集的tarces
 48     private List<TracerouteContainer> traces = new ArrayList<TracerouteContainer>();;
 49 
 50     @Override
 51     protected void onCreate(Bundle savedInstanceState) {
 52         super.onCreate(savedInstanceState);
 53         setContentView(R.layout.activity_main);
 54         et = (EditText) this.findViewById(R.id.input);
 55         searchButton = (Button) this.findViewById(R.id.search);
 56         searchButton.setOnClickListener(new View.OnClickListener() {
 57 
 58             @Override
 59             public void onClick(View v) {
 60                 // TODO Auto-generated method stub
 61                 new ExecuteTracerouteAsyncTask(MAX_TTL, et.getText().toString())
 62                         .execute();
 63             }
 64         });
 65     }
 66 
 67     private void showResultInLog() {
 68         for (TracerouteContainer container : traces) {
 69             Log.v("ccc", container.toString());
 70         }
 71     }
 72 
 73     /**
 74      * 这个任务就是来更新我们的后台log 日志 把所得到的traceroute信息打印出来。
 75      * 
 76      */
 77     private class ExecuteTracerouteAsyncTask extends
 78             AsyncTask<Void, Void, String> {
 79 
 80         private int maxTtl;
 81 
 82         private String url;
 83 
 84         public ExecuteTracerouteAsyncTask(int maxTtl, String url) {
 85             this.maxTtl = maxTtl;
 86             this.url = url;
 87         }
 88 
 89         /**
 90          * 后台所做的工作 本质就是调用 ping命令 来完成类似traceroute的功能
 91          */
 92         @Override
 93         protected String doInBackground(Void... params) {
 94             String res = "";
 95             try {
 96                 res = launchPing(url);
 97             } catch (IOException e1) {
 98                 // TODO Auto-generated catch block
 99                 e1.printStackTrace();
100             }
101             TracerouteContainer trace;
102 
103             if (res.contains(UNREACHABLE_PING) && !res.contains(EXCEED_PING)) {
104                 trace = new TracerouteContainer("", parseIpFromPing(res),
105                         elapsedTime);
106             } else {
107                 trace = new TracerouteContainer("", parseIpFromPing(res),
108                         ttl == maxTtl ? Float
109                                 .parseFloat(parseTimeFromPing(res))
110                                 : elapsedTime);
111             }
112 
113             InetAddress inetAddr;
114             try {
115                 inetAddr = InetAddress.getByName(trace.getIp());
116                 String hostname = inetAddr.getHostName();
117                 trace.setHostname(hostname);
118             } catch (UnknownHostException e) {
119                 e.printStackTrace();
120             }
121             traces.add(trace);
122             return res;
123         }
124 
125         private String launchPing(String url) throws IOException {
126             Process p;
127             String command = "";
128 
129             // 这个实际上就是我们的命令第一封装 注意ttl的值的变化 第一次调用的时候 ttl的值为1
130             String format = "ping -c 1 -t %d ";
131             command = String.format(format, ttl);
132 
133             long startTime = System.nanoTime();
134             // 实际调用命令时 后面要跟上url地址
135             p = Runtime.getRuntime().exec(command + url);
136             BufferedReader stdInput = new BufferedReader(new InputStreamReader(
137                     p.getInputStream()));
138 
139             String s;
140             String res = "";
141             while ((s = stdInput.readLine()) != null) {
142                 res += s + "\n";
143                 // 这个地方这么做的原因是 有的手机 返回的from 有的手机返回的是From所以要
144                 // 这么去判定 请求结束的事件 算一下 延时
145                 if (s.contains(FROM_PING) || s.contains(SMALL_FROM_PING)) {
146                     elapsedTime = (System.nanoTime() - startTime) / 1000000.0f;
147                 }
148             }
149 
150             // 调用结束的时候 销毁这个资源
151             p.destroy();
152 
153             if (res.equals("")) {
154                 throw new IllegalArgumentException();
155             }
156             // 第一次调用ping命令的时候 记得把取得的最终的ip地址 赋给外面的ipToPing
157             // 后面要依据这个ipToPing的值来判断是否到达ip数据报的 终点
158             if (ttl == 1) {
159                 ipToPing = parseIpToPingFromPing(res);
160             }
161             return res;
162         }
163 
164         @Override
165         protected void onPostExecute(String result) {
166             // 如果为空的话就截止吧 过程完毕
167             if (TextUtils.isEmpty(result)) {
168                 return;
169             }
170 
171             // 如果这一跳的ip地址与最终的地址 一致的话 就说明 ping到了终点
172             if (traces.get(traces.size() - 1).getIp().equals(ipToPing)) {
173                 if (ttl < maxTtl) {
174                     ttl = maxTtl;
175                     traces.remove(traces.size() - 1);
176                     new ExecuteTracerouteAsyncTask(maxTtl, url).execute();
177                 } else {
178                     // 如果ttl ==maxTtl的话 当然就结束了 我们就要打印出最终的结果
179                     showResultInLog();
180                 }
181             } else {
182                 // 如果比较的ip 不相等 哪就说明还没有ping到最后一跳。我们就需要继续ping
183                 // 继续ping的时候 记得ttl的值要加1
184                 if (ttl < maxTtl) {
185                     ttl++;
186                     new ExecuteTracerouteAsyncTask(maxTtl, url).execute();
187                 }
188             }
189             super.onPostExecute(result);
190         }
191 
192     }
193 
194     /**
195      * 从结果集中解析出ip
196      * 
197      * @param ping
198      * @return
199      */
200     private String parseIpFromPing(String ping) {
201         String ip = "";
202         if (ping.contains(FROM_PING)) {
203             int index = ping.indexOf(FROM_PING);
204 
205             ip = ping.substring(index + 5);
206             if (ip.contains(PARENTHESE_OPEN_PING)) {
207                 int indexOpen = ip.indexOf(PARENTHESE_OPEN_PING);
208                 int indexClose = ip.indexOf(PARENTHESE_CLOSE_PING);
209 
210                 ip = ip.substring(indexOpen + 1, indexClose);
211             } else {
212                 ip = ip.substring(0, ip.indexOf("\n"));
213                 if (ip.contains(":")) {
214                     index = ip.indexOf(":");
215                 } else {
216                     index = ip.indexOf(" ");
217                 }
218 
219                 ip = ip.substring(0, index);
220             }
221         } else {
222             int indexOpen = ping.indexOf(PARENTHESE_OPEN_PING);
223             int indexClose = ping.indexOf(PARENTHESE_CLOSE_PING);
224 
225             ip = ping.substring(indexOpen + 1, indexClose);
226         }
227 
228         return ip;
229     }
230 
231     /**
232      * 从结果集中解析出ip
233      * 
234      * @param ping
235      * @return
236      */
237     private String parseIpToPingFromPing(String ping) {
238         String ip = "";
239         if (ping.contains(PING)) {
240             int indexOpen = ping.indexOf(PARENTHESE_OPEN_PING);
241             int indexClose = ping.indexOf(PARENTHESE_CLOSE_PING);
242 
243             ip = ping.substring(indexOpen + 1, indexClose);
244         }
245 
246         return ip;
247     }
248 
249     /**
250      * 从结果集中解析出time
251      * 
252      * @param ping
253      * @return
254      */
255     private String parseTimeFromPing(String ping) {
256         String time = "";
257         if (ping.contains(TIME_PING)) {
258             int index = ping.indexOf(TIME_PING);
259 
260             time = ping.substring(index + 5);
261             index = time.indexOf(" ");
262             time = time.substring(0, index);
263         }
264 
265         return time;
266     }
267 
268 }

 

其实代码本身并没有多复杂 主要是要对linux有一定了解 另外要会在adb shell 下面调试你的命令结果。分析结果集。

 

posted @ 2015-01-19 15:57  希尔瓦娜斯女神  阅读(12781)  评论(2编辑  收藏  举报