Имя хоста Android не распознается при создании сокета TCP
Привет всем, я боролся с этим некоторое время сейчас и не могу найти решение.
В Java этот код работает:
InetAddress serverAddr = InetAddress.getByName("myservice.local");
Socket socket = new Socket(serverAddr, PoolServerAddress.DEFAULT_PORT);
То же самое в Android не работает. Я попытался удалить ".local" и добавить все необходимые разрешения в манифесте:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Я также попытался добавить блокировку многоадресной рассылки перед выполнением этого кода, но не смог решить эту проблему.
Я получаю следующие исключения:
06-03 13:18:14.438: W/System.err(31636): java.net.UnknownHostException: Unable to
resolve host <server_name>: No address associated with hostname
06-03 13:18:14.438: W/System.err(31636): at
java.net.InetAddress.lookupHostByName(InetAddress.java:434)
06-03 13:18:14.438: W/System.err(31636): at
java.net.InetAddress.getAllByNameImpl(InetAddress.java:239)
06-03 13:18:14.438: W/System.err(31636): at
java.net.InetAddress.getByName(InetAddress.java:292)
06-03 13:18:14.438: W/System.err(31636): at
com.example.jelly_test.MainActivity.connect(MainActivity.java:113)
06-03 13:18:14.438: W/System.err(31636): at
com.example.jelly_test.MainActivity$ResolvingThread.run(MainActivity.java:71)
06-03 13:18:14.438: W/System.err(31636): Caused by: libcore.io.GaiException:
getaddrinfo
failed: EAI_NODATA (No address associated with hostname)
06-03 13:18:14.438: W/System.err(31636): at libcore.io.Posix.getaddrinfo(Native
Method)
06-03 13:18:14.438: W/System.err(31636): at
libcore.io.ForwardingOs.getaddrinfo(ForwardingOs.java:55)
06-03 13:18:14.438: W/System.err(31636): at
java.net.InetAddress.lookupHostByName(InetAddress.java:415)
06-03 13:18:14.438: W/System.err(31636): ... 4 mor
Если я предоставляю ip вместо службы, она работает, но мне нужно предоставить имя. Также я попытался добавить JmDNS для разрешения имени в ip, но это медленно и работает только на внутренних службах в офисе, но некоторые из наших служб являются внешними по отношению к сети, но они все еще доступны с версией java, но не для Android. Могу ли я попробовать что-нибудь еще? Большое спасибо, поскольку у меня заканчиваются идеи....!
РЕДАКТИРОВАТЬ: я думаю, что моя проблема связана с тем, что есть ошибка в Android, и он принимает только полное доменное имя, например: myservice.company.com
1 ответ
Как и было запрошено, есть варианты, которые я нашел для временного решения этой проблемы, так как даже в последней версии Android (4.4 на момент написания статьи) ошибка все еще не была исправлена.
Убедитесь, что вы добавили все необходимые разрешения, указанные в вопросе, в манифест.
Вариант 1: Используйте библиотеку JmDNS, чтобы найти список доступных серверов и найти свой, если вы не знаете, как здесь хорошее руководство: http://home.heeere.com/tech-androidjmdns.html Этот метод медленный и Я обнаружил, что некоторые сервисы не показываются (их нужно рекламировать с помощью zeroconf)
Вариант 2. Попросите пользователей ввести полное доменное имя, например name.comany.com, вместо name.local.
Вариант 3: Это действительно хак, но если вы знаете, что полное доменное имя имеет тип someName.myComp.com, и пользователь ввел someName.local, вы должны удалить ".local" и попытаться угадать, что такое домен:
private String getTargetIp(String host) {
System.out.println("Will try to create socket with "+host);
MulticastLock multicastLock = null;
String ipToReturn = null;
Socket socket = null;
try {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
multicastLock = wifiManager.createMulticastLock("any_name");
multicastLock.acquire();
DhcpInfo info = wifiManager.getDhcpInfo();
int ip = info.ipAddress;
InetAddress local = getInetAddress(ip);
String localHostName = local.getCanonicalHostName();
Log.d(TAG, "Device ip addr1 = "+local +" name= "+localHostName);
String[] temp = localHostName.split("\\.");
//replace the first part with our name and obtain FQDN
String targetFullHostName=localHostName.replace(temp[0], host);
Log.d(TAG, "targetFullHostName = "+targetFullHostName);
//this is not mandatory but here we are trying to retrieve the ip address
//it maybe also a way to check if we guessed right
InetAddress serverAddr = InetAddress
.getByName(targetFullHostName);
socket = new Socket(serverAddr,
PoolServerAddress.DEFAULT_PORT);
ipToReturn = socket.getInetAddress().getHostAddress();
Log.d(TAG, "socket remote address=" + ipToReturn);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}catch(Exception e){
Log.e(TAG, "Other Exception");
}
finally {
if (multicastLock != null)
multicastLock.release();
if(socket!=null)
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return ipToReturn;
}
Вариант 4. Начиная с уровня API 16, вы можете использовать прослушиватель Android Service Discovery, чтобы просмотреть список доступных служб и найти тот, который вы ищете. Это работает аналогично JmDNS: вы обнаруживаете все службы и пытаетесь найти свои в списке. Вот ссылка на документ: http://developer.android.com/training/connect-devices-wirelessly/nsd.html