我需要的,如果一个设备具有完整的互联网接入,以可靠地检测,即用户不局限于一个强制网络门户 (也称围墙花园 ),即强制用户提交他们的凭证的形式,以获得完整的有限子网访问。
我的应用程序自动化认证过程,因此要知道完整的互联网接入是不启动登录活动之前可以是很重要的。
现在的问题不是如何检查网络接口并处于连接状态。 它是关于确保设备具有无限制上网,而不是沙盒内部网段。
所有到目前为止,我已经试过的方法都失败,因为连接到任何著名主持人不会抛出异常,只是返回一个有效的HTTP 200
,因为所有的请求路由到登录页面响应代码。
这里是所有我试过的方法,但他们都返回true
而不是false
由于上述原因解释说:
1:
InetAddress.getByName(host).isReachable(TIMEOUT_IN_MILLISECONDS);
isConnected = true; <exception not thrown>
2:
Socket socket = new Socket();
SocketAddress sockaddr = new InetSocketAddress(InetAddress.getByName(host), 80);
socket.connect(sockaddr, pingTimeout);
isConnected = socket.isConnected();
3:
URL url = new URL(hostUrl));
URLConnection urlConn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setRequestMethod("GET");
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
那么,如何做我确保我连接到一个实际的主机,而不是登录重定向页面? 很显然,我可以从“平”的主机我用检查实际响应主体,但它看起来并不像一个妥善的解决办法。
Answer 1:
作为参考,这里是从Android 4.0.1 AOSP代码库“官方”方法: WifiWatchdogStateMachine.isWalledGardenConnection() 。 我包括在未来的下方,以防万一链接中断的代码。
private static final String mWalledGardenUrl = "http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
private boolean isWalledGardenConnection() {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(mWalledGardenUrl); // "http://clients3.google.com/generate_204"
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(WALLED_GARDEN_SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// We got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) {
log("Walled garden check - probably not a portal: exception "
+ e);
}
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
这种方法依赖于特定的URL, mWalledGardenUrl = "http://clients3.google.com/generate_204"
总是返回一个204
响应代码。 如果DNS已经干扰了因为在这种情况下,这甚至会工作, 200
代码将被替代返回预期的204
。 我已经看到了一些圈养门户网站欺骗请求这个特定的URL,以防止互联网上的Android设备无法访问的消息。
谷歌有这个主题的变化:取http://www.google.com/blank.html
会返回一个200
码长度为零的响应体。 所以,如果你得到一个非空体,这将是另一种方式来弄清楚你是后面的围墙花园。
苹果拥有自己的网址,用于检测圈养门户网站:当网络已经启动IOS和MacOS设备将连接到像一个URL http://www.apple.com/library/test/success.html , http://attwifi.apple。 COM /库/测试/ success.html ,或http://captive.apple.com/hotspot-detect.html它必须返回的HTTP状态代码200
和包含体Success
。
注意 :此方法不会在限制Internet访问,如中国,整个国家是一个有围墙的花园 ,并在谷歌大多数/苹果业务被阻塞或过滤领域的工作。 其中的一些可能无法阻止: http://www.google.cn/generate_204
, http://g.cn/generate_204
, http://gstatic.com/generate_204
或http://connectivitycheck.gstatic.com/generate_204
-但这些都属于谷歌这样不能保证工作。
Answer 2:
另一种可能的解决方案可能是通过HTTPS连接和检查目标证书。 不知道围墙花园通过HTTPS实际上提供登录页面或刚落的连接。 在这两种情况下,你应该能够看到你的目的地是不是你所期望的一个。
当然,你也有TLS和证书检查的开销。 这就是身份验证连接的价格,很遗憾。
Answer 3:
我相信防止重定向您的连接将工作。
URL url = new URL(hostUrl));
HttpURLConnection httpConn = (HttpURLConnection)url.openConnection();
/* This line prevents redirects */
httpConn.setInstanceFollowRedirects( false );
httpConn.setAllowUserInteraction( false );
httpConn.setRequestMethod( "GET" );
httpConn.connect();
responseCode = httpConn.getResponseCode();
isConnected = responseCode == HttpURLConnection.HTTP_OK;
如果不工作的话,我觉得这样做是为了检查响应的主体的唯一途径。
Answer 4:
这已经实现在Android 4.2.2+版本 - 我发现他们的方法快速,有趣的:
CaptivePortalTracker.java检测围墙花园如下 - 尝试连接到www.google.com/generate_204 - 检查HTTP响应204
如果检查失败,我们是在一个有围墙的花园。
private boolean isCaptivePortal(InetAddress server) {
HttpURLConnection urlConnection = null;
if (!mIsCaptivePortalCheckEnabled) return false;
mUrl = "http://" + server.getHostAddress() + "/generate_204";
if (DBG) log("Checking " + mUrl);
try {
URL url = new URL(mUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
// we got a valid response, but not from the real google
return urlConnection.getResponseCode() != 204;
} catch (IOException e) {
if (DBG) log("Probably not a portal: exception " + e);
return false;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
Answer 5:
如果你已经使用了retrofit
,你可以通过做retrofit
。 只是做一个ping.html页面,并使用改装发送头要求,并确保您的HTTP客户端配置如下图所示:( followRedirects(false)
的部分是最重要的部分)
private OkHttpClient getCheckInternetOkHttpClient() {
return new OkHttpClient.Builder()
.readTimeout(2L, TimeUnit.SECONDS)
.connectTimeout(2L, TimeUnit.SECONDS)
.followRedirects(false)
.build();
}
然后建立自己的改装象下面这样:
private InternetCheckApi getCheckInternetRetrofitApi() {
return (new Retrofit.Builder())
.baseUrl("[base url of your ping.html page]")
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.client(getCheckInternetOkHttpClient())
.build().create(InternetCheckApi.class);
}
您InternetCheckApi.class会是这样:
public interface InternetCheckApi {
@Headers({"Content-Typel: application/json"})
@HEAD("ping.html")
Call<Void> checkInternetConnectivity();
}
那么你可以使用它象下面这样:
getCheckInternetOkHttpClient().checkInternetConnectivity().enqueue(new Callback<Void>() {
public void onResponse(Call<Void> call, Response<Void> response) {
if(response.code() == 200) {
//internet is available
} else {
//internet is not available
}
}
public void onFailure(Call<Void> call, Throwable t) {
//internet is not available
}
}
);
请注意,您的互联网检查HTTP客户端必须是从主HTTP客户端独立。
Answer 6:
这是最好的在这里完成在AOSP: https://github.com/aosp-mirror/platform_frameworks_base/blob/6bebb8418ceecf44d2af40033870f3aabacfe36e/core/java/android/net/captiveportal/CaptivePortalProbeResult.java#L61
https://github.com/aosp-mirror/platform_frameworks_base/blob/e3a0f42e8e8678f6d90ddf104d485858fbb2e35b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
private static final String GOOGLE_PING_URL = "http://google.com/generate_204";
private static final int SOCKET_TIMEOUT_MS = 10000;
public boolean isCaptivePortal () {
try {
URL url = new URL(GOOGLE_PING_URL);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
urlConnection.getInputStream();
return (urlConnection.getResponseCode() != 204)
&& (urlConnection.getResponseCode() >= 200)
&& (urlConnection.getResponseCode() <= 399);
} catch (Exception e) {
// for any exception throw an exception saying check was unsuccesful
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
请注意,这可能不是一个代理网络和一些更高级的如AOSP网址上工作需要做
文章来源: How to check for unrestricted Internet access? (captive portal detection)