簡體   English   中英

通過Wi-Fi P2P使用網絡服務發現時無法連接到Android設備

[英]Can't connect to Android devices when using Network Service Discovery through Wi-Fi P2P

大家! 我正在開發一款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的實例變量。

看起來您只需要在兩端使用正確的端口號。

你使用零,從文檔意味着:

端口號為0表示通常從臨時端口范圍自動分配端口號。

因此,在創建ServerSocket時,請確保它正在偵聽其他設備用於啟動連接的同一端口:

private static final int port = 6770; 

//.....

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

再次! 我終於設法讓我的應用程序正常運行。 這就是我所做的:

  • 硬編碼端口號;
  • 在ConnectionInfoListener實現中獲取組所有者地址時,請確保它是否是正在使用的設備的IP地址。 如果不是,請將客戶端套接字連接到組所有者地址; 否則,讓你的應用程序等待傳入連接;
  • 盡快初始化ServerSocket(例如,當應用程序啟動時)。

為了在建立Wi-Fi Direct連接后獲取設備的實際IP地址,我在“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;
}

“p2pInt”是在Utils類中聲明的私有靜態字符串costant:

private final static String p2pInt = "p2p-p2p0"

但是,在我的應用程序中,我更改了“p2p-wlan0”中的“p2p-p2p0”字符串,因為看起來我的Wi-Fi Direct設備的網絡接口具有(不同的)名稱。 我希望這可以幫助任何嘗試創建使用Wi-Fi Direct連接的應用程序的開發人員。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM