【问题标题】:Can't connect to Android devices when using Network Service Discovery through Wi-Fi P2P通过 Wi-Fi P2P 使用网络服务发现时无法连接到 Android 设备
【发布时间】:2016-11-17 06:50:05
【问题描述】:

大家! 我正在开发一个 Android 应用程序,允许与附近已安装此应用程序的设备聊天。为了实现这一点,我正在使用 Wi-Fi P2P API 和网络服务发现来搜索附近的此类设备。 我已经编写了用于在服务启动的线程中搜索附近设备的代码。当检测到设备时,Service 将其(通过广播意图)发送到显示目前检测到的设备的 Activity。 检测到的设备被添加到 recyclerView 中,当用户按下其中一个时,必须建立与此类设备的连接。 Wi-Fi Direct 连接建立成功(即 WifiP2pManager.connect() 方法成功)并捕获 WIFI_P2P_CONNECTION_CHANGED_ACTION。 在广播接收器中,当捕获到这样的广播意图时,将执行以下代码:

NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
            if (networkInfo.isConnected()) {

                mManager.requestConnectionInfo(mChannel, connectionInfoListener); }

通过 requestConnectionInfo() 方法,我可以获得有关连接的更多信息,例如我尝试连接的设备的 IP 地址。 为了获取此类信息,我为该方法提供了 WifiP2pManager.ConnectionInfoListener 的实现,由 connectionInfoListener 变量表示。 这是我实现WifiP2pManager.ConnectionInfoListener的代码:

private WifiP2pManager.ConnectionInfoListener connectionInfoListener = new WifiP2pManager.ConnectionInfoListener() {

        @Override
        public void onConnectionInfoAvailable(WifiP2pInfo info) {

            InetAddress deviceIP = info.groupOwnerAddress;


            int port = servicesConnectionInfo.get(device);

            ConnectThread connectThread = new ConnectThread(deviceIP, port, device);
            connectThread.start();

“device”是我的 BroadcastReceiver 实现的一个实例变量,现在并不重要。相反,重要的是 ConnectThread 线程。这是处理连接两个设备之间的套接字所需代码的线程。当我尝试连接到检测到的设备时,ConnectThread 在其 run() 方法中创建一个新的 ChatConnection 实例,将先前获得的 IP 地址和端口号传递给此构造函数:

 public ChatConnection(InetAddress srvAddress, int srvPort, String macAddress) throws IOException {
        ...

        connSocket = new Socket(srvAddress, srvPort);

        ...

    }

这就是问题发生的地方。当我在物理设备上测试我的应用时,我得到的只是这个异常:

W/System.err: java.net.ConnectException: failed to connect to /192.168.49.1 (port 6770): connect failed: ECONNREFUSED (Connection refused)

当然,我也在第二台物理设备上安装了我的应用程序,它被成功检测到并且 Wi-Fi Direct 连接成功建立。但是,当谈到这行代码时:

connSocket = new Socket(srvAddress, srvPort);

抛出异常... 我为这个问题的长度道歉,但我想尽可能清楚。 非常感谢您的帮助。

编辑:我忘了提及初始化 ServerSocket 的代码。
ServerSocket 在启用 Wi-Fi 后立即启动的线程中进行初始化。
也就是说,当 WifiP2pBroadcastReceiver(应用程序的 Service 的一个内部类,它扩展了 BroadcastReceiver)捕获到 WIFI_P2P_STATE_CHANGED_ACTION Intent 时,它会检查 Wi-Fi 是否启用,如果启用,它会启动 ServerSocket 所在的线程:

public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();

        if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {

            int statoWiFi = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            if (statoWiFi == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {

                mNsdService = new NsdProviderThread();
                mNsdService.start();
            }

ServerSocket在NsdProviderThread的run()方法中初始化:

public void run() {
        ...

        try {
            server = new ServerSocket(0);
        } catch (IOException ex) {

            return;
        }

        ...

        while (!Thread.currentThread().isInterrupted()) {

            Socket clientSocket = null;
            try {
                clientSocket = server.accept();
            } catch (IOException ex) {

                break;
            }
            try {

                ChatConnection chatConn = new ChatConnection(clientSocket);
                synchronized (connections) {
                    connections.add(chatConn);
                }

            } catch (IOException ex) {

                continue;
            }
        }

“server”是NsdProviderThread的一个实例变量,声明为ServerSocket。

【问题讨论】:

  • 你需要一个ServerSocket来接受来自其他设备的Socket连接......
  • 感谢您的评论。我刚刚编辑了我的问题并添加了有关 ServerSocket 的更多信息。

标签: android wifip2p nsd


【解决方案1】:

看起来你只需要在两端使用正确的端口号。

您使用的是零,来自the documentation 的意思是:

端口号为 0 表示端口号自动 分配,通常来自临时端口范围。

因此,当您创建 ServerSocket 时,请确保它正在侦听其他设备用来启动连接的同一端口:

private static final int port = 6770; 

//.....

try {
    server = new ServerSocket(port);
} catch (IOException ex) {
    ex.printStackTrace();
}

【讨论】:

  • 我尝试像这样在 ServerSocket 中硬编码端口号: server = new ServerSocket(6770);我在 ChatConnection 的构造函数中做了同样的事情: connSocket = new Socket(srvAddress, 6770);现在,当应用程序进入该行代码时,不再抛出异常,但应用程序只是挂在那里(没有错误,没有异常),就像永远等待建立连接一样......
【解决方案2】:

再次!我终于设法让我的应用程序正常工作。这是我所做的:

  • 硬编码端口号;
  • 在 ConnectionInfoListener 实现中获取组所有者地址时,请确保它是正在使用的设备的 IP 地址。如果不是,则将客户端套接字连接到组所有者地址;否则,让您的应用等待传入连接;
  • 尽快初始化ServerSocket(例如,当应用程序启动时)。

为了在建立 Wi-Fi Direct 连接后获取设备的实际 IP 地址,我使用了我在 this 项目(由原始 Android WiFiDirectdemo 派生)中找到的这个函数“Utils”类:

public static String getLocalIPAddress() {
    /*
     * modified from:
     * 
     * http://thinkandroid.wordpress.com/2010/03/27/incorporating-socket-programming-into-your-applications/
     * 
     * */
    try {
        for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                InetAddress inetAddress = enumIpAddr.nextElement();

                String iface = intf.getName();
                if(iface.matches(".*" +p2pInt+ ".*")){
                    if (inetAddress instanceof Inet4Address) { // fix for Galaxy Nexus. IPv4 is easy to use :-)
                        return getDottedDecimalIP(inetAddress.getAddress());
                    }
                }
            }
        }
    } catch (SocketException ex) {
        Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
    } catch (NullPointerException ex) {
        Log.e("AndroidNetworkAddressFactory", "getLocalIPAddress()", ex);
    }
    return null;
}

"p2pInt" 是一个私有静态字符串常量,在 Utils 类中声明为:

private final static String p2pInt = "p2p-p2p0"

但是,在我的应用程序中,我更改了“p2p-wlan0”中的“p2p-p2p0”字符串,因为它看起来像我的 Wi-Fi Direct 设备的网络接口具有该(不同的)名称。 我希望这可以帮助任何尝试创建使用 Wi-Fi Direct 连接的应用程序的开发人员。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-19
    • 1970-01-01
    • 2018-06-14
    • 2016-08-17
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    相关资源
    最近更新 更多