我正在编写一个可以通过主机名在本地网络中找到主机的应用程序。在c ++ WIN32中,我使用来自Ws2tcpip.h的getaddrinfo(),它总是在我尝试过的每个网络上工作。在android java中,我使用java.Net.InetAddress中的getByName()来获取主机名的IP。这个android实现在每个网络中都不起作用。特别是在我的办公室网络中,我自己的机器名为" PC01"这是行不通的。它不返回IP地址,如果我输入" PC01"它会抛出UnknownHostException。
所以我尝试了一些东西:我从另一台机器上调试了我的c ++ WIN32应用程序而不是PC01,让我的机器PC01将数据包发送到我调试代码的机器上,这样我就可以通过检查来手动查找主机名传入数据包的属性。除外,它是" PC01"。虽然我对我的android java应用程序做了完全相同的操作,但getHostname()和getCanonicalHostname()方法都返回了PC01 IP的字符串represantation而不是其主机名的字符串。所以现在很清楚为什么getByName()无法找到我的机器,因为它被解析为" PC01的IP"而不是" PC01"。 经过一些研究后,我发现Java / Android正在使用的命名服务可能存在问题。由于c ++使用WINS,这似乎对我来说很好,java应用程序的命名服务不能正确解析主机名(我还有其他这种行为的例子,所以我的机器/网络不是唯一的一!)。
我的第一个问题:为了在每个网络中正确解析主机名,是否有getHostName()的替代品或其他命名服务的替代品?
我的NDK解决方法:正如我已经解释的那样,我认为解析在java api中失败了,所以我想使用c / netdb.h中的gethostbyname()函数进行NDK解决方法。所以我写了一个示例应用程序,看看它是否有效。这是我的代码:
Simple MyActivity,它将主机名的硬编码字符串传递给本机函数:
package david.test;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
String hostname = "PC01";
tv.setText(stringFromJNI(hostname));
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI(String hostname);
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
}
和将jstring转换为std:string的本机函数代码执行gethostbynme()操作并将结果返回到jstring:
#include <jni.h>
#include <string>
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_david_test_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */, jstring hostname) {
//convert to std::string
const jsize len = env->GetStringUTFLength(hostname);
const char* strChars = env->GetStringUTFChars(hostname, (jboolean *)0);
std::string hostnameStr(strChars, (unsigned long) len);
env->ReleaseStringUTFChars(hostname, strChars);
//end convert
struct hostent* result;
result = gethostbyname(hostnameStr.c_str());
char* ip = result->h_addr_list[0];
return env->NewStringUTF(ip);
}
对此代码不起作用的是&#39;结果&#39;永远是NULL。所以我的第二个问题:编写NDK代码是否有问题或我的计划不起作用(因此java中的本机代码也使用与普通java代码相同的名称服务)?
编辑2017-09-08:
解决了我的NDK实施问题,但我仍然有同样的“怪异”问题。虽然行为。方案:使用以下代码,我想通过本机方法检查我的机器的主机名是否得到正确解析。为此,我使用函数getaddrinfo和getnameinfo(在c / c ++ / uwp / win32中编写时,它们在每个设备上都能正常工作)。所以我想用这个代码做的就是先看看我通过ip解析主机名得到了什么结果,然后是恶cersa。 java代码中的主要活动保持与上面相同,只更改了本机部分:
让给定的主机名成为我机器的主机名(PC01),让给定的带编码的IP成为我机器的IP(IP_MACHINE)。注意:互联网的所有权限都在appmanifest中授予。
#include <jni.h>
#include <string>
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_david_test_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */, jstring hostname) {
//convert to std::string
const jsize len = env->GetStringUTFLength(hostname);
const char* strChars = env->GetStringUTFChars(hostname, (jboolean *)0);
// hostnameStr == hostname of my machine
std::string hostnameStr(strChars, (unsigned long) len);
env->ReleaseStringUTFChars(hostname, strChars);
//end convert
// get ip by hostname:
struct addrinfo hints, *servinfo;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 6;
hints.ai_flags = 2;
rv = getaddrinfo(hostnameStr.c_str(), NULL, &hints, &servinfo);
// rv == 7
char* error = (char*)gai_strerror(rv);
// error == "No address asociated with hostname"
// get hostname by ip:
struct sockaddr_in sa;
sa.sin_family = AF_INET;
inet_pton(AF_INET, IP_MACHINE, &sa.sin_addr);
char node[NI_MAXHOST];
int res = getnameinfo((struct sockaddr*)&sa, sizeof(sa), node, sizeof(node), NULL, NULL, NI_NOFQDN);
// res == 0
char* error2 = (char*) gai_strerror(res);
// error2 == "Success"
return env->NewStringUTF(node);
// node == "PC01.domain.local"
}
结论:
NI_NOFQDN
标志表示返回的主机名应该没有特定的域,但它会随之返回(domain.local)。 有了这个新的结论和新的测试,你能想到它为什么不能在android / java和furhtermore工作的原因我如何解决它或我如何解决它?说实话,这实际上是一件很简单的事情,为什么它不能按原样运作。
编辑2017-09-15:
为了证明我认为这是java的问题,我写了一个简单的测试类来测试Eclipse using jdk
中java的getByName()函数。 它在我的网络中正常工作,主机名无法通过android getByName解析!所以现在我可以说这是一个android问题。这是我使用的代码:
package testpackage;
import java.net.InetAddress;
public class TestClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
InetAddress ia;
try{
ia = InetAddress.getByName("PC01");
}
catch (Exception e){
ia = null;
}
if(ia != null){
System.out.println(ia.getHostAddress().toString());
}
else{
System.out.println("nope!");
}
}
}
结果:
IP of PC01
编辑2018-04-17:
到目前为止,我还没有找到解决问题的方法。我认为Google Play商店中的应用程序正在成功执行此操作 - 因此必须有解决方案来解决我的问题。
我当前的想法:我想模仿Windows客户端为将主机名解析为IP(如WINS)所做的步骤。我怎么能这样做(可能在低级套接字编程上)?