android的ping的实现丢包率的获取

最近做的ANdroid项目是有关于利用ping的方法去获取丢包率。

方案一(失败):

1、ping.c文件在JNI的实现。ping.c文件与其相关文件从Busybox源码(busybox-1.19.2)里拿,或是Android源码的\external\ping\目录下拿。

在jni下编译自己的.so文件。但是实验不成功。

追踪方法:用log在.c打印信息

需要声明

#include <android/log.h>
#define  LOG_TAG    "zyp"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

且要在Android.mk文件添加LOCAL_LDLIBS :=-llog(需要放在include $(CLEAR_VARS)后面的任意位置)

就可以使用:

LOGI("ping= %f",a);打印自己要的信息

追踪发现在创建ICMP协议的socket会失败出错。查找资源原因是必须程序是Root权限才可以。

采用办法是且要在
Java:Process process = Runtime.getRuntime().exec(“su”);

也是失败。原因是因为Runtime.getRuntime().exec(“su”);只是开启一个“Root”的进程,程序还是没有获得Root权限去创建Socket。

方案二(需要Root):

把jni的ping.c与相关文件编译成可执行的二进制文件,Android.mk文件采用Busybox源码(busybox-1.19.2)里拿,或是Android源码的\external\ping\目录下的Android.mk文件,用cygwin编译,编译成可执行文件ping。拷贝发到工程的assets目录下。

复制assets目录下的ping到私有文件目录下/data/data/pakeage_/files/ping

/**
     * 复制assets目录下的ping到私有文件目录下ping下。
     */
private boolean cpPingLib(){
String path = TestPing.this.getApplicationContext().getFilesDir()
          .getAbsolutePath()+ "/ping";   //data/data/包名/files/
File file = new File(path);
if(file.exists()){
return true;
}
FileOutputStream out = null;
InputStream in = null ;
try {
in = TestPing.this.getAssets().open("ping");  //从assets目录下复制
out = new FileOutputStream(file);
int length = -1;
byte[] buf = new byte[1024];
while ((length = in.read(buf)) != -1){
out.write(buf, 0, length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}finally{
try {
out.flush();
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return true;
}

/**
* 执行chmod 777 ping
*/
private boolean chmodExec() {
Process process = null;
        try {
        String cmd="chmod 777 "+TestPing.this.getApplicationContext()
        .getFilesDir().getAbsolutePath()+ "/ping";
        process = Runtime.getRuntime().exec(cmd); //切换到root帐号
            process.waitFor();
        } catch (Exception e) {
            return false;
        } finally {
            try {
                process.destroy();
            } catch (Exception e) {
            }
        }
return true;
}


然后就可以ping了(需要Root):

/**
     * 运行命令:"/data/data/ping -c [发包次数] -s [包大小] [ip地址]";
     * @return 丢包率
     */
    private int beginPing(String address, int countPage, int countDatePake) {
    	String returnMsg = "";
        Process process = null;
        DataOutputStream os = null;
        try {
        	String cmd=getApplicationContext().getFilesDir()
 		           .getAbsolutePath()+"/ping -c "+countPage+" -s "+
        			countDatePake +" "+address+ "\n";
        	process = Runtime.getRuntime().exec("su"); //切换到root帐号
        	os = new DataOutputStream(process.getOutputStream());
            InputStreamReader r = new InputStreamReader(
                    process.getInputStream()); 
            LineNumberReader returnData = new LineNumberReader(r);
            os.writeBytes(cmd + "\n");
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
            String line = "";
            while ((line = returnData.readLine()) != null) {
            	if(line.contains("packet loss is "))
            	returnMsg += (line);
            }
        } catch (Exception e) {
            return -1;
        } finally {
            try {
                process.destroy();
            } catch (Exception e) {
            }
        }
//returnMsg = "100 packets transmitted, 0 received, packet loss is 100% ..."
        int s = returnMsg.indexOf("packet loss is");
        s += 14;
        while((++s)<returnMsg.length()){
        	if((returnMsg.charAt(s)<48) || (returnMsg.charAt(s)>57))
        		continue;
        	int n = s;
        	while( ++n<returnMsg.length() ){
        		if(returnMsg.charAt(n)!='%') continue;
        		return Integer.valueOf(returnMsg.substring(s, n));
        	}
        }
        return -1;
    }   


方案三(不确定)

之所以不直接用系统的ping,是因为系统ping打印的信息是不确定的:

ping -c4 192.168.1.118
PING 192.168.1.118 (192.168.1.118) 56(84) bytes of data.
64 bytes from 192.168.1.118: icmp_seq=1 ttl=64 time=31.4 ms
64 bytes from 192.168.1.118: icmp_seq=2 ttl=64 time=2.05 ms
64 bytes from 192.168.1.118: icmp_seq=3 ttl=64 time=1.45 ms
64 bytes from 192.168.1.118: icmp_seq=4 ttl=64 time=9.78 ms
--- 192.168.1.118 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 1.452/11.178/31.422/12.140 ms

其中0% packet loss在不同的系统是不同格式的,所以才有方案一与方案二,方案一、二是创建自己的ping,自己的ping打印的信息结果有确定性。

方案三还是有成功的概率,去把ping打印的结果进行分析,用”,“进行分离,把含有loss与%在就把间的数字读取即可(如果ping打印的信息没有loss与%就会失败了):

/**
     * 运行命令:"ping -c [发包次数] -s [包大小] [ip地址]";
     * @return 丢包率
     */
private int pingExec(String address, int countPage, int countDatePake) {
    Process process = null;
    String returnMsg = "";
    try {
    String cmd="ping -c "+countPage+" -s "+countDatePake +" "+address;
    process = Runtime.getRuntime().exec(cmd);
InputStreamReader r = new InputStreamReader(
process.getInputStream());
LineNumberReader returnData = new LineNumberReader(r);
String line = "";
while ((line = returnData.readLine()) != null) {
// returnMsg += line;
if(line.contains("loss") && line.contains("%") ){
returnMsg += line;
}
}
// returnMsg = "100 packets transmitted, 0 received, 
// packet loss is 100%  , time 99007ms";

} catch (IOException e) {
e.printStackTrace();
return -1;
}finally {
            try {
                process.destroy();
            } catch (Exception e) {
            }
        }
    int index = -1;
String splitStr[]= returnMsg.split(",");
for(int i=0;i<splitStr.length;i++){
if(splitStr[i].contains("loss") && splitStr[i].contains("%")){
returnMsg = splitStr[i];
index = returnMsg.indexOf('%');
break;
}
}
int m = index;
while( m-->0 ){
if((returnMsg.charAt(m)<48) || (returnMsg.charAt(m)>57)){
break;
}
}
if(((m+1)==index) ||((m+1)<0) || (index>(returnMsg.length()-1))){
return -1;
}
return Integer.valueOf(returnMsg.substring(m+1,index));
    }


方案四,发送UDP的包,需要开启服务器的Echo(是端口7)

private int pingUDP(String ipaddress, int countPage, int countDatePake){
int countErr=0;
int count=0;
DatagramSocket socket = null;
StringBuffer strSend = new StringBuffer();
DatagramPacket packetSend = null;
try {
String sensStr = "boys or girls,you can receive me";//32 bit
if(strSend.length()>0)
strSend.delete(0,strSend.length());

int n = countDatePake/32;
for(int i=0;i<n;i++){
strSend.append(sensStr);
}
int surplus = countDatePake - 32*n;
if( surplus>0 )strSend.append(sensStr.substring(0, surplus));

socket = new DatagramSocket(5000);
socket.setSoTimeout(5000);
InetAddress serverAddress = InetAddress.getByName(ipaddress);
byte data [] = strSend.toString().getBytes();
packetSend = new DatagramPacket(data,data.length,serverAddress,7);
//调用socket对象的send方法,发送数据

} catch (Exception e) {
e.printStackTrace();
}

DatagramPacket packetRe = null;
try {
while((!stop) && (count<countPage)){
if(socket==null)break;
socket.send(packetSend);
count++;
byte data [] = new byte[strSend.length()];
packetRe = new DatagramPacket(data,data.length);
socket.receive(packetRe);//如果客户端没有发送数据,该进程就停滞在这里
if(!Arrays.equals(packetRe.getData(),strSend.toString().getBytes())){
countErr++;
}
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
if((count == countPage) && ( count>0)){
return ((countErr*100)/count);
}
return -1;
}

pingUDP()函数是将接受到的数据与发送的数据进行对比,数据一样的话就成功咯【Arrays.equals(packetRe.getData(),strSend.toString().getBytes())进行比较】。

方案二最好。我是先实行方案二,若是失败就采用方案三,否则就方案四了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章