最近做的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())进行比较】。
方案二最好。我是先实行方案二,若是失败就采用方案三,否则就方案四了。