android 4 DnsPinger一点分析
来源:互联网 发布:apache jmeter 下载 编辑:程序博客网 时间:2024/05/21 19:46
http://androidxref.com/4.4_r1/xref/frameworks/base/core/java/android/net/DnsPinger.java
在android 4.0.3上,WifiWatchDogStateMachine使用到了DnsPinger
在android 4.1.1上,就没有再看到DnsPinger使用的地方了,但是代码还在
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import com.android.internal.util.Protocol;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Performs a simple DNS "ping" by sending a "server status" query packet to the
* DNS server. As long as the server replies, we consider it a success.
* <p>
* We do not use a simple hostname lookup because that could be cached and the
* API may not differentiate between a time out and a failure lookup (which we
* really care about).
* <p>
*
* @hide
*/
public final class DnsPinger extends Handler {// 派生自Handler
private static final boolean DBG = false;
private static final int RECEIVE_POLL_INTERVAL_MS = 200;
private static final int DNS_PORT = 53;
/** Short socket timeout so we don't block one any 'receive' call */
private static final int SOCKET_TIMEOUT_MS = 1;
/** Used to generate IDs */
private static final Random sRandom = new Random();// 用于生成DNS查询报文Header中的随机数
private static final AtomicInteger sCounter = new AtomicInteger();
private ConnectivityManager mConnectivityManager = null;
private final Context mContext;
private final int mConnectionType;
private final Handler mTarget;
private final ArrayList<InetAddress> mDefaultDns;
private String TAG;
//Invalidates old dns requests upon a cancel
private AtomicInteger mCurrentToken = new AtomicInteger();// 取消DNS查询请求时起作用
private static final int BASE = Protocol.BASE_DNS_PINGER;
/**
* Async response packet for dns pings.
* arg1 is the ID of the ping, also returned by {@link #pingDnsAsync(InetAddress, int, int)}
* arg2 is the delay, or is negative on error.
*/
public static final int DNS_PING_RESULT = BASE;
/** An error code for a {@link #DNS_PING_RESULT} packet */
public static final int TIMEOUT = -1;
/** An error code for a {@link #DNS_PING_RESULT} packet */
public static final int SOCKET_EXCEPTION = -2;
/**
* Send a new ping via a socket. arg1 is ID, arg2 is timeout, obj is InetAddress to ping
*/
private static final int ACTION_PING_DNS = BASE + 1;
private static final int ACTION_LISTEN_FOR_RESPONSE = BASE + 2;
private static final int ACTION_CANCEL_ALL_PINGS = BASE + 3;
private List<ActivePing> mActivePings = new ArrayList<ActivePing>();
private int mEventCounter;
private class ActivePing {
DatagramSocket socket;
int internalId;
short packetId;
int timeout;
Integer result;
long start = SystemClock.elapsedRealtime();
}
/* Message argument for ACTION_PING_DNS */
private class DnsArg {
InetAddress dns;
int seq;
DnsArg(InetAddress d, int s) {
dns = d;
seq = s;
}
}
public DnsPinger(Context context, String TAG, Looper looper,
Handler target, int connectionType) {
super(looper);// DnsPinger依附于传入的looper
this.TAG = TAG;
mContext = context;
mTarget = target;// DNS查询结果,通知给target
mConnectionType = connectionType;
if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {// 传入的连接类型是否有效
throw new IllegalArgumentException("Invalid connectionType in constructor: "
+ connectionType);
}
mDefaultDns = new ArrayList<InetAddress>();
mDefaultDns.add(getDefaultDns());// 初始化DNS
mEventCounter = 0;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ACTION_PING_DNS:// 1、处理DNS查询请求
DnsArg dnsArg = (DnsArg) msg.obj;
if (dnsArg.seq != mCurrentToken.get()) {// 校验发送查询请求消息中的id,和当前mCurrentToken控制id是否一致,一致处理;不一致即取消
break;
}
try {
ActivePing newActivePing = new ActivePing();
InetAddress dnsAddress = dnsArg.dns;
newActivePing.internalId = msg.arg1;
newActivePing.timeout = msg.arg2;
newActivePing.socket = new DatagramSocket();// 构造UDP socket,并绑定到本地主机地址上的任意可用端口
// Set some socket properties
newActivePing.socket.setSoTimeout(SOCKET_TIMEOUT_MS);// 设置socket超时1ms
// Try to bind but continue ping if bind fails
try {
newActivePing.socket.setNetworkInterface(NetworkInterface.getByName(
getCurrentLinkProperties().getInterfaceName()));// setNetworkInterface是android在java上的扩展接口
} catch (Exception e) {
loge("sendDnsPing::Error binding to socket " + e);
}
newActivePing.packetId = (short) sRandom.nextInt();// 生成short类型的随机数,作为DNS查询报文Header中的ID表示,2byte
byte[] buf = mDnsQuery.clone();
buf[0] = (byte) (newActivePing.packetId >> 8);// 取随机数高8位
buf[1] = (byte) newActivePing.packetId;// 取随机数低8位
// Send the DNS query
DatagramPacket packet = new DatagramPacket(buf, buf.length, dnsAddress, DNS_PORT);// 构造发送数据包
if (DBG) {
log("Sending a ping " + newActivePing.internalId +
" to " + dnsAddress.getHostAddress()
+ " with packetId " + newActivePing.packetId + ".");
}
newActivePing.socket.send(packet);
mActivePings.add(newActivePing);// 添加到DNS查询活动列表
mEventCounter++;
sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
RECEIVE_POLL_INTERVAL_MS);// 延迟200ms接收DNS应答报文,注意mEventCounter
} catch (IOException e) {
sendResponse(msg.arg1, -9999, SOCKET_EXCEPTION);
}
break;
case ACTION_LISTEN_FOR_RESPONSE:// 2、处理DNS接收报文
if (msg.arg1 != mEventCounter) {// 注意mEventCounter
break;
}
for (ActivePing curPing : mActivePings) {
try {
/** Each socket will block for {@link #SOCKET_TIMEOUT_MS} in receive() */
byte[] responseBuf = new byte[2];
DatagramPacket replyPacket = new DatagramPacket(responseBuf, 2);// 构造UDP接收报文
curPing.socket.receive(replyPacket);
// Check that ID field matches (we're throwing out the rest of the packet)
if (responseBuf[0] == (byte) (curPing.packetId >> 8) &&
responseBuf[1] == (byte) curPing.packetId) {// 校验DNS请求报文中的随机数,和应答报文中的随机数是否一致
curPing.result = (int) (SystemClock.elapsedRealtime() - curPing.start);// result为请求到应答的耗时} else {
if (DBG) {
log("response ID didn't match, ignoring packet");
}
}
} catch (SocketTimeoutException e) {
// A timeout here doesn't mean anything - squelsh this exception
} catch (Exception e) {
if (DBG) {
log("DnsPinger.pingDns got socket exception: " + e);
}
curPing.result = SOCKET_EXCEPTION;
}
}
Iterator<ActivePing> iter = mActivePings.iterator();
while (iter.hasNext()) {
ActivePing curPing = iter.next();
if (curPing.result != null) {// 如果已经收到DNS应答报文,则通知外部,并关闭socket,从活动列表中清除
sendResponse(curPing.internalId, curPing.packetId, curPing.result);
curPing.socket.close();
iter.remove();
} else if (SystemClock.elapsedRealtime() > curPing.start + curPing.timeout) {// 如果超时,处理
sendResponse(curPing.internalId, curPing.packetId, TIMEOUT);
curPing.socket.close();
iter.remove();
}
}
if (!mActivePings.isEmpty()) {// 还有未收到应答的DNS请求,则循环延迟200ms等待接收
sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
RECEIVE_POLL_INTERVAL_MS);
}
break;
case ACTION_CANCEL_ALL_PINGS:
for (ActivePing activePing : mActivePings)
activePing.socket.close();
mActivePings.clear();
break;
}
}
/**
* Returns a list of DNS addresses, coming from either the link properties of the
* specified connection or the default system DNS if the link properties has no dnses.
* @return a non-empty non-null list
*/
public List<InetAddress> getDnsList() {// 1、获取DNS列表
LinkProperties curLinkProps = getCurrentLinkProperties();
if (curLinkProps == null) {
loge("getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
return mDefaultDns;
}
Collection<InetAddress> dnses = curLinkProps.getDnses();
if (dnses == null || dnses.size() == 0) {
loge("getDns::LinkProps has null dns - returning default");
return mDefaultDns;
}
return new ArrayList<InetAddress>(dnses);
}
/**
* Send a ping. The response will come via a {@link #DNS_PING_RESULT} to the handler
* specified at creation.
* @param dns address of dns server to ping
* @param timeout timeout for ping
* @return an ID field, which will also be included in the {@link #DNS_PING_RESULT} message.
*/
public int pingDnsAsync(InetAddress dns, int timeout, int delay) {// 2、发送DNS查询请求
int id = sCounter.incrementAndGet();// 原子变量, 同步控制
sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout,
new DnsArg(dns, mCurrentToken.get())), delay);
return id;
}
public void cancelPings() {// 3、取消DNS查询请求
mCurrentToken.incrementAndGet();
obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
}
private void sendResponse(int internalId, int externalId, int responseVal) {
if(DBG) {
log("Responding to packet " + internalId +
" externalId " + externalId +
" and val " + responseVal);
}
mTarget.sendMessage(obtainMessage(DNS_PING_RESULT, internalId, responseVal));
}
private LinkProperties getCurrentLinkProperties() {
if (mConnectivityManager == null) {
mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
}
return mConnectivityManager.getLinkProperties(mConnectionType);
}
private InetAddress getDefaultDns() {// 获取默认DNS,TODO 可以通过反射获取出来吧
String dns = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DEFAULT_DNS_SERVER);
if (dns == null || dns.length() == 0) {
dns = mContext.getResources().getString(
com.android.internal.R.string.config_default_dns_server);
}
try {
return NetworkUtils.numericToInetAddress(dns);
} catch (IllegalArgumentException e) {
loge("getDefaultDns::malformed default dns address");
return null;
}
}
private static final byte[] mDnsQuery = new byte[] {// 对应DNS查询报文结构
0, 0, // [0-1] is for ID (will set each time)
1, 0, // [2-3] are flags. Set byte[2] = 1 for recursion desired (RD) on. Currently on.
0, 1, // [4-5] bytes are for number of queries (QCOUNT)
0, 0, // [6-7] unused count field for dns response packets
0, 0, // [8-9] unused count field for dns response packets
0, 0, // [10-11] unused count field for dns response packets Header中12字节,到此
3, 'w', 'w', 'w', // 长度(1字节)+ N字节内容
6, 'g', 'o', 'o', 'g', 'l', 'e',
3, 'c', 'o', 'm',
0, // null terminator of address (also called empty TLD) // 终止符为0
0, 1, // QTYPE, set to 1 = A (host address) // 查询资源记录类型,0x01指定计算机IP地址
0, 1 // QCLASS, set to 1 = IN (internet) // 分类,指定信息的协议组,0x01指定Internet类别
};
private void log(String s) {
Log.d(TAG, s);
}
private void loge(String s) {
Log.e(TAG, s);
}
}
参考:
http://09105106.blog.163.com/blog/static/2483578201342584441807/
http://tools.ietf.org/html/rfc1035
http://www.freesoft.org/CIE/Topics/77.htm
http://www.isnowfy.com/dns-frame/
http://www.2cto.com/Article/201408/323317.html
0 0
- android 4 DnsPinger一点分析
- android dumpsys的一点分析
- Android多媒体分析(六)对AudioManager的一点补充
- Android多媒体分析(六)对AudioManager的一点补充
- Android多媒体分析(六)对AudioManager的一点补充
- Android多媒体分析(六)对AudioManager的一点补充
- Android下NFC驱动初始化过程的一点分析
- Android多媒体分析(六)对AudioManager的一点补充
- Android多媒体分析(六)对AudioManager的一点补充
- TranslateAnimation 一点分析
- TranslateAnimation 一点分析
- irq 的一点分析
- 三层架构的一点分析
- security cookie的一点分析
- 关于 include 的一点分析
- 做需求分析一点心得
- 软件分析的一点心得
- 源码分析的一点感悟
- 手势解锁代码实现
- bzoj1251 序列终结者
- Java反射机制入门
- 手势
- hitTest方法底层实现
- android 4 DnsPinger一点分析
- hitTest方法底层实现(2)
- 图片水印
- 使用WinWebMail 简单搭建一个局域网邮件服务 并 测试
- 地址译码方式
- Swing 线程之SwingUtilities.invokeLater()
- [Unity框架]PureMVC在unity中的简单使用
- 安装或更新vmware tools
- 无限循环的图片轮播器