上網找了一下,一般對於ping的建議是直接利用java內建的InetAddress.isReachable()來做,實際上在ping內部網路的伺服器時,是沒問題的,但是如果要ping位於外部網路的伺服器時,就會失敗而回傳False。
Android內部的實作是在libcore/luni/src/main/java/java/net/InetAddress.java裡,這裡就很簡單的建立socket,然後試著連到指定位址的port 7,如果可以連,或者是伺服器明確地拒絕,就視為伺服器存在,可以連線。這就解釋了為什麼無法ping位於外部網路的伺服器,因為ISP為了安全或是其他考量,而不允許。我分別以python與java寫了與Android實作相似的程式去實驗,的確都不行。
import sys import socket if len(sys.argv)<2: print( "Need at least 1 parameters." ) print( "Usage: {0} host".format( sys.argv[0] ) ) sys.exit(-1) r = False try: s = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) s.settimeout(5) s.connect( (sys.argv[1], 7) ) r = True except socket.error, ex: if ex.errno==111: r = True else: print( ex ) if r: print( "{0} is reachable.".format( sys.argv[1] ) ) else: print( "{0} is NOT reachable.".format( sys.argv[1] ) )
import java.net.InetAddress; import java.net.UnknownHostException; import java.io.IOException; class Ping { public static void main(String[] args) { InetAddress in; try { in = InetAddress.getByName(args[0]); boolean result = in.isReachable(5000); if (result) { System.out.println("Response OK"); } else { System.out.println("Response fail"); } } catch (UnknownHostException e) { System.out.println(e.getMessage()); } catch (IOException e) { System.out.println(e.getMessage()); } } }
Java裡只能建立 stream(TCP) 或 dgram(UDP) 的socket,那麼只能用JNI,用C寫ping了,但經過實驗結果,發現會因為權限的關係而無法建立socket,原來要建立raw與IPPROTO_ICMP的socket,需要root權限。一般linux裡,非root使用者可以使用ping,是因為ping加上了setuid權限,才能使用。在Android裡,要不就是建立service,要不就是設法為ping加上setuid,否則是都無法使用的。
沒有留言:
張貼留言