Android - getByName()无法解析主机名/如何将主机名解析为Android网络中的IP?

时间:2017-08-28 07:31:06

标签: android networking ip hostname android-networking

我正在编写一个可以通过主机名在本地网络中找到主机的应用程序。在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实施问题,但我仍然有同样的“怪异”问题。虽然行为。方案:使用以下代码,我想通过本机方法检查我的机器的主机名是否得到正确解析。为此,我使用函数getaddrinfogetnameinfo(在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"
}

结论:

  1. getnameinfo有效。但它无法正常工作,因为NI_NOFQDN标志表示返回的主机名应该没有特定的域,但它会随之返回(domain.local)。
  2. getaddrinfo根本不起作用。我尝试了PC01和PC01.domain.local,因为getnameinfo函数返回了域添加 - 所以getnameinfo函数可以解析主机名并且getaddrinfo函数无法执行它更奇怪。
  3. 好像我的计划不起作用,如果使用本机代码,也会存在阻碍android中正确的hostnameresolution的原因。
  4. 有了这个新的结论和新的测试,你能想到它为什么不能在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)所做的步骤。我怎么能这样做(可能在低级套接字编程上)?

0 个答案:

没有答案