Треш и угар: android, ping и java

Кодинг под мобильные платформы, это то еще удовольствие. Наверное, начиная с времен J2ME. С надеждой жду, когда Jolla Mobile выкатит свою продукцию на рыкок, ибо iOS и android это та еще пакость. Ну а пока выбираю дройд, как меньшее из зол и более вменяемое.
Потребовалось мне на днях пингануть приложением интернеты, проверить живо ли оно да в зависимости от этого пошаманствовать. Свежа история, на прошлой неделе человек рассказывал мне про лютые извращения с SCNetworkCheckReachabilityByName на iOS для этой цели, пришла теперь моя очередь познакомиться с этим адом в контексте андроеда.

Раскурив документацию, наваяв активити, ресивер броадкастов и вытащив пинг в отдельный тред, который стартовал из службы, дабы не блокировать рендер гуи, я нарисовал нижеследующий код.
(Сразу предупреждаю, я системный программер, знающий жабу чуть менее, чем никак, потому оно вполне может быть достойно занесения на говнокод)

	/* proper way. works on local ips, sucks on 8.8.8.8 */
	public int doInAvaliable(int count, int delay, String host) {
		InetAddress in;
		in = null;
		int ok = 0;
		int i;
		for (i = 0; i < count; i++) {
			if (!active)
				return ok;
			try {
				in = InetAddress.getByName(host);
			} catch (UnknownHostException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
 
			try {
				if (in.isReachable(delay)) {
					Log.d(TAG, "Network looks fine");
					doBroadcast(scanResultsReceiver.action_ping_ok);
					ok++;
				} else {
					Log.d(TAG, "Timeout, it's dead");
					doBroadcast(scanResultsReceiver.action_ping_fail);
				}
				try {
					Thread.sleep(delay);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
 
				}
			} catch (IOException e) {
				Log.d(TAG, "Caught an exception: " + e.toString());
			}
		}
		return ok;
	}

Все просто, пингуем десять раз посылая уведомления броадкастами гую. И все бы хорошо, даже пинганул IP роутера. Да вот сетевой узел для пинга, 8.8.8.8 так и не осилил пингануть. Фейл, да и все тут.
Чешем голову, читаем документацию, stackoverflow, материм {Гугл, Oracle, Sun}, и выдавливаем методом частичной копипасты из себя такой код:

		/* Is crappy, doesn't work at all */
	public int doSocket(int count, int delay, String host) {
 
		Socket socket = null;
		boolean reachable = false;
		int i, ok = 0;
		for (i = 0; i < count; i++) {
			if (!active)
				return ok;
			try {
				Thread.sleep(delay);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
 
			}
			try {
				socket = new Socket(host, 80);
				ok++;
				doBroadcast(scanResultsReceiver.action_ping_ok);
			} catch (IOException e) {
				doBroadcast(scanResultsReceiver.action_ping_fail);
			} finally {
				if (socket != null)
					try {
						socket.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}
 
		}
 
		return ok;
 
	}

На выходе получаем еще один фейл. Не, ну не вызывать же из жабы консольную утилиту ping и не парсить ее exit code? А пришлось.

public int doPing(int count, int delay, String host) {
		int i, r;
		int ok = 0;
		for (i = 0; i < count; i++) {
			r = 3;
			if (!active)
				return 0;
			try {
				Thread.sleep(delay);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				r = pingHost(host, delay);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if (r == 0) {
				doBroadcast(scanResultsReceiver.action_ping_ok);
				ok++;
			} else
				doBroadcast(scanResultsReceiver.action_ping_fail);
		}
		return ok;
	}
 
	/**
	 * Ping a host and return an int value of 0 or 1 or 2 0=success, 1=fail,
	 * 2=error
	 * 
	 * Does not work in Android emulator and also delay by '1' second if host
	 * not pingable In the Android emulator only ping to 127.0.0.1 works
	 * 
	 * @param String
	 *            host in dotted IP address format
	 * @return
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public static int pingHost(String host, int timeout) throws IOException,
			InterruptedException {
		Runtime runtime = Runtime.getRuntime();
		timeout /= 1000;
		String cmd = "ping -c 1 -W " + timeout + " " + host;
		Process proc = runtime.exec(cmd);
		Log.d(TAG, cmd);
		proc.waitFor();
		int exit = proc.exitValue();
		return exit;
	}

И это будучи хоть и грязным хаком, но заработало без проблем.
 

Треш и угар: android, ping и java: 7 комментариев

  1. Вот это говнокод, просто жесть. А просто http запрос сделать на удалённый сервер?

    1. Потому, что этим проверяется живо ли интернет соединение к которому мы подцепились, или же BSка представляет собой труп. Для этого ICMP кажется наиболее естественным механизмом, так как он работает даже в случаях, когда сами интернеты хотят http авторизацию, как например Beeline_Free точки доступа.
      isReachable как оказалось, пытается сделать не ICMP, а проверяет echo на 7м порту.

  2. Жалкое оправдание. А слабо проверить, что по http пришло то, что должно придти, а не форма авторизации.

  3. Более того, в Андроиде это 1-й строкой делается, для этого там специальная ф-ция есть.

  4. Функцию в студию. Если это тупо проверка connectivity, то если ты раскуришь доку, то поймешь что это совсем не то, что надо. Оно имеет обыкновение прочихиваться через 40 секунд после того, как интернеты появились. Это то, что я проверил в первую очередь. Потому нет, не катит.
    Проверка того, что http пришел нужный мне пока не требовалась, нужно было только ICMP.

  5. Да, я это видел. ICMP на целевых точках не блочится. Проверка по http у меня есть, разве что в упрощенном виде через сокет на 80 порт (см. код выше). Попробую еще через httpUrlConnection сегодня. Проблема в том, что работает оно странно, и иногда на живой сети выдает отрицательный результат первые 40-60 секунд соединения, в то время как ping выдает результат моментально, как только поднялся интерфейс. А мне надо быстрее. Почему — пока еще не разобрался.

Добавить комментарий