通过特定连接在 android 应用程序中发送流量
Sending traffic in android app via particular connection
我正在开发一个 android 应用程序,我特别需要在其中仅通过 WiFi 建立 HTTP 连接。 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 文档,我应该能够 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.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.call(AsyncTask.java:295)
W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err: at android.os.AsyncTask$SerialExecutor.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 没有互联网时,Android 不允许我在我的应用程序中使用 wifi 连接。
问题原来是由于后台 运行 的 VPN 应用程序导致的,即使使用 OkHttpClient 之前似乎可以正常工作也会发生这种情况。
我找到了使用 OkHttpClient 的解决方案。基本上,当使用 network.openConnection
时会出现某种错误,它可能会尝试绑定到小于 1024 的端口,这在 Linux 中是不可能的,除非你是 root。
虽然出于某种原因,如果我执行 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 日志中看到两个不同的 public IP 地址 - 一个是您的 wifi public IP 地址,另一个是您的数据 public IP 地址。但请确保在您连接的 wifi 网络中,仅允许 text.whatsiyourip.org 并阻止其他所有内容。如果您有一个支持多个 SSID 和防火墙的路由器,这就很简单了。
我正在开发一个 android 应用程序,我特别需要在其中仅通过 WiFi 建立 HTTP 连接。 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 文档,我应该能够 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.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.call(AsyncTask.java:295)
W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err: at android.os.AsyncTask$SerialExecutor.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 没有互联网时,Android 不允许我在我的应用程序中使用 wifi 连接。
问题原来是由于后台 运行 的 VPN 应用程序导致的,即使使用 OkHttpClient 之前似乎可以正常工作也会发生这种情况。
我找到了使用 OkHttpClient 的解决方案。基本上,当使用 network.openConnection
时会出现某种错误,它可能会尝试绑定到小于 1024 的端口,这在 Linux 中是不可能的,除非你是 root。
虽然出于某种原因,如果我执行 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 日志中看到两个不同的 public IP 地址 - 一个是您的 wifi public IP 地址,另一个是您的数据 public IP 地址。但请确保在您连接的 wifi 网络中,仅允许 text.whatsiyourip.org 并阻止其他所有内容。如果您有一个支持多个 SSID 和防火墙的路由器,这就很简单了。