通过Wi-Fi P2P使用网络服务发现时无法连接到Android设备

时间:2016-11-04 00:58:34

标签: android wifip2p nsd

大家好! 我正在开发一款Android应用,可以与已安装此应用的附近设备聊天。为了实现这一目标,我正在使用Wi-Fi P2P API和网络服务发现来搜索附近的设备。 我编写了用于在服务启动的线程中搜索附近设备的代码。当检测到设备时,服务将其(通过广播意图)发送到活动,该活动显示到目前为止检测到的设备。 检测到的设备被添加到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(扩展BroadcastReceiver的应用程序服务的内部类)捕获WIFI_P2P_STATE_CHANGED_ACTION意图时,它会检查是否启用了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”是声明为ServerSocket的NsdProviderThread的实例变量。

2 个答案:

答案 0 :(得分:0)

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

您使用的是零,the documentation表示:

  

端口号为0表示端口号是自动的   通常从短暂的港口范围分配。

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

private static final int port = 6770; 

//.....

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

答案 1 :(得分:0)

又来了!我终于设法让我的应用运行起来了。这就是我所做的:

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

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

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;
}

&#34; p2pInt&#34;是在Utils类中声明为:

的私有静态字符串costant
private final static String p2pInt = "p2p-p2p0"

然而,在我的应用中,我已经改变了&#34; p2p-p2p0&#34;字符串在&#34; p2p-wlan0&#34;因为看起来我的Wi-Fi Direct设备的网络接口有这个(不同的)名称。 我希望这可以帮助任何尝试创建使用Wi-Fi Direct连接的应用的开发者。