0

私は特にWiFi経由でのみHTTP接続を行う必要があるAndroidアプリを開発しています。Android L以降では接続関連の変更が多かったようです。

これは私が使用しているコードです:

ConnectivityManager manager = (ConnectivityManager)
ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
Network[] allNetworks = manager.getAllNetworks();

for(Network network : allNetworks) {
  NetworkInfo info = manager.getNetworkInfo(network);
  if(info.getType() == ConnectivityManager.TYPE_WIFI && info.getState() == NetworkInfo.State.CONNECTED) {
   System.out.println("FOUND WIFI NETWORK!");

   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
     manager.bindProcessToNetwork(network);
   }

   app.network = network;
   break;
 }
 }

ConnectivityManager および Network API docs に従って、 networkObject.openConnection を実行して、そのネットワークにバインドされた HttpUrlConnection を取得できるはずです。しかし、私はこのタイプの例外を取得しています

W/System.err: java.net.SocketException: Binding socket to network 586 failed: EPERM (Operation not permitted)
W/System.err:     at android.net.Network.bindSocket(Network.java:362)
W/System.err:     at android.net.Network.bindSocket(Network.java:331)
W/System.err:     at android.net.Network$NetworkBoundSocketFactory.createSocket(Network.java:182)
W/System.err:     at com.android.okhttp.internal.http.SocketConnector.connectRawSocket(SocketConnector.java:155)
W/System.err:     at com.android.okhttp.internal.http.SocketConnector.connectCleartext(SocketConnector.java:67)
W/System.err:     at com.android.okhttp.Connection.connect(Connection.java:152)
W/System.err:     at com.android.okhttp.Connection.connectAndSetOwner(Connection.java:185)
W/System.err:     at com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
W/System.err:     at com.android.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:341)
W/System.err:     at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:330)
W/System.err:     at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:248)
W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:437)
W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:114)
W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:245)
W/System.err:     at com.mypkg.myapp.utils.HttpRequestBackground.doInBackground(HttpRequestBackground.java:90)
W/System.err:     at com.mypkg.myapp.utils.HttpRequestBackground.doInBackground(HttpRequestBackground.java:38)
W/System.err:     at android.os.AsyncTask$2.call(AsyncTask.java:295)
W/System.err:     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err:     at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
W/System.err:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
W/System.err:     at java.lang.Thread.run(Thread.java:818)
W/System.err: Caused by: android.system.ErrnoException: Binding socket to network 586 failed: EPERM (Operation not permitted)

これらは、マニフェストで宣言されたネットワーク関連のアクセス許可です

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

ここで何が問題なのかよくわかりません。

これを行う理由: Android では、データが有効で、接続先の AP にインターネットがない場合、アプリで Wi-Fi 接続を使用できません。

4

1 に答える 1

4

この問題は、バックグラウンドで実行されていた VPN アプリが原因であることが判明し、以前は動作していたように見えた OkHttpClient でも発生していました。


OkHttpClient を使用して解決策を見つけました。基本的に、 を使用すると何らかのバグが発生し、network.openConnection1024 未満のポートにバインドしようとする可能性があります。これは、root でない限り、Linux では不可能です。

何らかの理由で、それを実行しnetwork.getSocketFactory()て OkHttpClient に渡すと、正常に機能します。

テストに使用したコードは次のとおりです。

package com.nileshgr.networktest;

import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NetworkRequest.Builder requestbuilder = new NetworkRequest.Builder();
        requestbuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);

        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);

        cm.requestNetwork(requestbuilder.build(), new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                System.out.println("wifi network found");
                testSocket(network);
            }
        });
    }

    private void testSocket(Network network) {

        // client one, should go via wifi
        OkHttpClient.Builder builder1 = new OkHttpClient.Builder();
        builder1.socketFactory(network.getSocketFactory());
        OkHttpClient client1 = builder1.build();
        Request request1 = new Request.Builder().url("http://text.whatisyourip.org").build();

        Callback cb = new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                System.out.println("success");
                System.out.println(response.body().string());
            }
        };

        System.out.println("sending via wifi network");

        client1.newCall(request1).enqueue(cb);

        System.out.println("Sending via data network");

        // client 2 should go via data
        OkHttpClient client2 = new OkHttpClient();
        Request request2 = new Request.Builder().url("http://text.whatisyourip.org").build();
        client2.newCall(request2).enqueue(cb);
    }
}

adb ログに 2 つの異なるパブリック IP アドレスが表示されます。1 つは Wi-Fi パブリック IP アドレスで、もう 1 つはデータ パブリック IP アドレスです。ただし、接続している Wi-Fi ネットワークで、text.whatsiyourip.org のみを許可し、それ以外はすべてブロックしていることを確認してください。複数の SSID をサポートし、ファイアウォールを備えたルーターを使用している場合、それは非常に簡単です。

于 2016-12-17T12:57:31.607 に答える