みんな!このアプリがインストールされている近くのデバイスとチャットできる 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 のインスタンス変数です。