1

みんな!このアプリがインストールされている近くのデバイスとチャットできる Android アプリを開発しています。これを実現するために、私は Wi-Fi P2P API と Network Service Discovery を使用して、そのような近くのデバイスを検索しています。サービスによって開始されたスレッドで近くのデバイスを検索するためのコードを作成しました。デバイスが検出されると、サービスはそれを (ブロードキャスト インテントを介して) これまでに検出されたデバイスを表示するアクティビティに送信します。検出されたデバイスは recyclerView に追加され、ユーザーがそのうちの 1 つを押したときに、そのデバイスへの接続を確立する必要があります。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 アドレスなど、接続に関する詳細情報を取得できます。そのような情報を取得するために、connectionInfoListener 変数で示される WifiP2pManager.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();

「デバイス」は、現在重要ではない BroadcastReceiver の実装のインスタンス変数です。代わりに、重要なのは ConnectThread スレッドです。これは、2 つのデバイス間のソケットを接続するために必要なコードを処理するスレッドです。検出されたデバイスに接続しようとすると、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)

もちろん、2 台目の物理デバイスにもアプリをインストールしましたが、これは正常に検出され、Wi-Fi Direct 接続が正常に確立されました。しかし、このコード行になると:

connSocket = new Socket(srvAddress, srvPort);

その例外がスローされます...この質問が長くなって申し訳ありませんが、可能な限り明確にしたかったのです。助けてくれて本当にありがとう。

編集: ServerSocket を初期化するためのコードについて言及するのを忘れていました。
ServerSocket は、Wi-Fi が有効になるとすぐに開始されるスレッドで初期化されます。
つまり、WifiP2pBroadcastReceiver (BroadcastReceiver を拡張するアプリの Service の内部クラス) が 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 のインスタンス変数です。

4

2 に答える 2

0

また!ようやくアプリを機能させることができました。これが私がやったことです:

  • ポート番号をハードコーディングします。
  • ConnectionInfoListener 実装でグループ所有者のアドレスを取得するときは、それが使用中のデバイスの IP アドレスであるかどうかを確認してください。そうでない場合は、クライアント ソケットをグループ オーナーのアドレスに接続します。それ以外の場合は、アプリが着信接続を待機するようにします。
  • できるだけ早く ServerSocket を初期化します (たとえば、アプリの起動時)。

Wi-Fi Direct 接続が確立された後にデバイスの実際の IP アドレスを取得するために、このプロジェクトで見つけたこの関数 (元の 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 接続を使用するアプリを作成しようとしているすべての開発者に役立つことを願っています。

于 2016-11-16T22:50:05.410 に答える
0

両端で正しいポート番号を使用するだけでよいようです。

ゼロを使​​用しています。これは、ドキュメントから次のことを意味します。

ポート番号 0 は、通常はエフェメラル ポート範囲からポート番号が自動的に割り当てられることを意味します。

そのため、ServerSocket を作成するときは、他のデバイスが接続を開始するために使用するポートと同じポートでリッスンしていることを確認してください。

private static final int port = 6770; 

//.....

try {
    server = new ServerSocket(port);
} catch (IOException ex) {
    ex.printStackTrace();
}
于 2016-11-05T03:28:09.413 に答える