簡介
本文主要介紹基于Android 7.0 的IpReachabilityMonitor機(jī)制
IpReachabilityMonitor簡介
* Monitors on-link IP reachability and notifies callers whenever any on-link addresses of interest appear to have become unresponsive.
//監(jiān)視鏈路IP的可達(dá)性旺拉;無論任何時(shí)候羡玛,一旦所關(guān)注的鏈路地址變的不響應(yīng)時(shí)垮衷,就會(huì)通知調(diào)用者杰赛。
This code does not concern itself with "why" a neighbour might have become unreachable. Instead, it primarily reacts to the kernel's notion of IP reachability for each of the neighbours we know to be critically important to normal network connectivity. As such, it is often "just the messenger":the neighbours about which it warns are already deemed by the kernel to have become unreachable.
//IpReachabilityMonitor的代碼實(shí)現(xiàn)邏輯不關(guān)心為什么一個(gè)neighbour網(wǎng)絡(luò)變?yōu)椴豢蛇_(dá)。相反兽泄,他主要反映在內(nèi)核層面中每一個(gè)neighbour網(wǎng)絡(luò)的IP可達(dá)性概念或狀態(tài)漓概。我們知道這個(gè)IP可達(dá)性狀態(tài)對(duì)于正常網(wǎng)絡(luò)連接來說是關(guān)鍵重要的。因此病梢,IpReachabilityMonitor通常僅是“信使”:它警告neighbours網(wǎng)絡(luò)已經(jīng)被內(nèi)核kernel視為不可達(dá)胃珍。
IpReachabilityMonitor工作方式
How it works:
1. The "on-link neighbours of interest" found in a given LinkProperties instance are added to a "watch list" via #updateLinkProperties().This usually means all default gateways and any on-link DNS servers.
//1、在給定的LinkProperties實(shí)例中找到關(guān)注的鏈路neighbours網(wǎng)絡(luò)蜓陌,這些neighbours網(wǎng)絡(luò)被通過updateLinkProperties()方法添加到一個(gè)"watch list"中觅彰。這通常意味著所有默認(rèn)網(wǎng)關(guān)和鏈路DNS服務(wù)器。
2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH,RTM_DELNEIGH), watching only for neighbours in the watch list.
//2钮热、我們連續(xù)不斷地監(jiān)聽netlink消息(RTM_NEWNEIGH,RTM_DELNEIGH)填抬,僅僅關(guān)注在watch list中的neighbours網(wǎng)絡(luò)。
- A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and even NUD_PROBE is perfectly normal; we merely record the new state.
//一個(gè)neighbour網(wǎng)絡(luò)進(jìn)入NUD_REACHABLE, NUD_STALE, NUD_DELAY和NUD_PROBE狀態(tài)隧期,均是完全正吵兆啵現(xiàn)象,我們只是記錄最新的狀態(tài)厌秒。
- A neighbour's entry may be deleted (RTM_DELNEIGH), for example due to garbage collection.? This is not necessarily of immediate concern; we record the neighbour as moving to NUD_NONE.
//一個(gè)neighbour條目可能被刪除(RTM_DELNEIGH)读拆,例如由于垃圾回收。這個(gè)沒有必要進(jìn)行立即關(guān)聯(lián)鸵闪。我們記錄被搬移到NUD_NONE狀態(tài)的neighbour網(wǎng)絡(luò)檐晕。
- A neighbour transitioning to NUD_FAILED (for any reason) is? critically important and is handled as described below in #4.
//一個(gè)neighbour網(wǎng)絡(luò)被轉(zhuǎn)移到NUD_FAILED(任何原因)是極其重要的,這個(gè)相應(yīng)的操作將會(huì)在下面#4描述蚌讼。
3. All on-link neighbours in the watch list can be forcibly "probed" by calling #probeAll(). This should be called whenever it is important to verify that critical neighbours on the link are still reachable, e.g. when roaming between BSSIDs.
//3辟灰、可以通過調(diào)用probeAll()方法來強(qiáng)制執(zhí)行probed所有在watch list 中的鏈路neighbours網(wǎng)絡(luò)。不管什么時(shí)候篡石,對(duì)于想驗(yàn)證鏈路上關(guān)鍵neighbour網(wǎng)絡(luò)依然可達(dá)芥喇,這個(gè)方法是重要的,應(yīng)該被調(diào)用凰萨,比如當(dāng)在不同的BSSID之間Roaming的時(shí)候继控。
- The kernel will send unicast ARP requests for IPv4 neighbours and unicast NS packets for IPv6 neighbours.? The expected replies will likely be unicast.
//內(nèi)核kernel將為IPv4 neighbour 網(wǎng)絡(luò)發(fā)送單播ARP請(qǐng)求和為IPv6 neighbour網(wǎng)絡(luò)發(fā)送單播NS數(shù)據(jù)包械馆。預(yù)期的答復(fù)可能是單播。
- The forced probing is done holding a wakelock. The kernel may,however, initiate probing of a neighbor on its own, i.e. whenever a neighbour has expired from NUD_DELAY.
//強(qiáng)制probing 必須持有一個(gè)wakelock才可以執(zhí)行武通。然而霹崎,內(nèi)核kernel也許引發(fā)自己的一個(gè)neighbor
網(wǎng)絡(luò)的probing,比如一個(gè)neighbour網(wǎng)絡(luò)已經(jīng)過期了(在進(jìn)入NUD_DELAY狀態(tài)后過期)冶忱。
- The kernel sends:
&1(/proc/sys/net/ipv{4,6}/neigh//ucast_solicit) number of probes (usually 3) every? ? ? ? &2(/proc/sys/net/ipv{4,6}/neigh//retrans_time_ms)number of milliseconds (usually 1000ms).
This normally results in 3 unicast packets, 1 per second.
//內(nèi)核Kernel每&2秒發(fā)送&1次Probe 單播請(qǐng)求尾菇。通常結(jié)果是每1秒發(fā)送3次單播請(qǐng)求。
- If no response is received to any of the probe packets, the kernel marks the neighbour as being in state NUD_FAILED, and the listening process in #2 will learn of it.
//如果對(duì)于所有的Probe 請(qǐng)求囚枪,都沒有接受到響應(yīng)派诬,內(nèi)核kernel會(huì)將neighbour網(wǎng)絡(luò)標(biāo)記為NUD_FAILED狀態(tài),在#2中描述的監(jiān)聽進(jìn)程將會(huì)學(xué)習(xí)它链沼。
4. We call the supplied Callback#notifyLost() function if the loss of a neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to become incomplete (a loss of provisioning).
//4千埃、如果處于NUD_FAILED狀態(tài)的neighbour網(wǎng)絡(luò)的丟失將會(huì)導(dǎo)致Ipv4 或者Ipv6配置變得不完整(配置丟失),我們將會(huì)調(diào)用提供的Callback#notifyLost()函數(shù)忆植。
- For example, losing all our IPv4 on-link DNS servers (or losing our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6) provisioning; Callback#notifyLost() would be called.
//比如丟失所有IPv4鏈路DNS服務(wù)器(或者丟失僅有的Ipv6默認(rèn)網(wǎng)關(guān))會(huì)構(gòu)成Ipv4(Ipv6)配置丟失放可。
Callback#notifyLost()函數(shù)將會(huì)被調(diào)用。
- Since it can be non-trivial to reacquire certain IP provisioning state it may be best for the link to disconnect completely and reconnect afresh.
//因?yàn)橹匦芦@取特定的IP配置狀態(tài)是非常重要的朝刊。最好的方式是完全斷開連接耀里,然后進(jìn)行重連。
IpReachabilityMonitor實(shí)現(xiàn)
1拾氓、在給定的LinkProperties實(shí)例中找到關(guān)注的鏈路neighbours網(wǎng)絡(luò)冯挎,這些neighbours網(wǎng)絡(luò)被通過updateLinkProperties()方法添加到一個(gè)"watch list"中。這通常意味著所有默認(rèn)網(wǎng)關(guān)和鏈路DNS服務(wù)器咙鞍。
public void updateLinkProperties(LinkProperties lp)
? ? ? mLinkProperties = new LinkProperties(lp);? ? ? ? ? ?
???? MapnewIpWatchList = new HashMap<>();? ? ? ? ? ?
???? final Listroutes = mLinkProperties.getRoutes();
? //將處于鏈接狀態(tài)的默認(rèn)網(wǎng)關(guān)加入IpWatchList
??? for (RouteInfo route : routes) {
??????????? if (route.hasGateway()) {
?????????????????????? InetAddress gw = route.getGateway();
??????????????????????????? if (isOnLink(routes, gw)) {
??????????????????????????????????????????? newIpWatchList.put(gw, getNeighborStateLocked(gw);}} }
//將處于鏈接狀態(tài)的DNS服務(wù)器加入IpWatchList
for (InetAddress nameserver : lp.getDnsServers()) {
?????????? if (isOnLink(routes, nameserver)) {
??????????????????? newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver));}}
mIpWatchList = newIpWatchList;
mIpWatchListVersion++;
2房官、我們連續(xù)不斷地監(jiān)聽netlink消息(RTM_NEWNEIGH,RTM_DELNEIGH),僅僅關(guān)注在watch list中的neighbours網(wǎng)絡(luò)续滋。
public IpReachabilityMonitor(Context context, String ifName, Callback callback,
AvoidBadWifiTracker tracker) throws IllegalArgumentException {
...
//NetlinkSocketObserver類是用來建立Socket連接翰守,不斷地接受來自kernel的netlink消息,并并解析消息疲酌,判讀是否加入IpWatchList中蜡峰。具體信息可以參考下面NetlinkSocketObserver部分解析
mNetlinkSocketObserver = new NetlinkSocketObserver();
mObserverThread = new Thread(mNetlinkSocketObserver);
mObserverThread.start();
...
}
3、可以通過調(diào)用probeAll()方法來強(qiáng)制執(zhí)行probed所有在watch list中的鏈路neighbours網(wǎng)絡(luò)朗恳。不管什么時(shí)候湿颅,對(duì)于想驗(yàn)證鏈路上關(guān)鍵neighbour網(wǎng)絡(luò)依然可達(dá),這個(gè)方法是重要的粥诫,應(yīng)該被調(diào)用油航,比如當(dāng)在不同的BSSID之間Roaming的時(shí)候。
//將IpWatchList中Key集合取出來怀浆,然后通過調(diào)用probeNeighbor(int ifIndex, InetAddress ip)驗(yàn)證相應(yīng)的neighbour是否可達(dá)谊囚。
public void probeAll() {
//將IpWatchList中Key集合賦值給ipProbeList
Set<InetAddress>? ipProbeList = new HashSet();
ipProbeList.addAll(mIpWatchList.keySet());
for (InetAddress target : ipProbeList) {
final int returnValue = probeNeighbor(mInterfaceIndex, target);
/// M: Add UDP packet for ARP retry
//下面是MTK添加的策略怕享,針對(duì)probeNeighbor失敗的情況下,使用UDP重新觸發(fā)一次ARP請(qǐng)求
if (returnValue != 0) {
// Use UDP broadcast to trigger ARP procedure.
sendUdpBroadcast(target);}}}
private static int probeNeighbor(int ifIndex, InetAddress ip):
對(duì)于具體接口Index號(hào)上的給定Ip地址秒啦,使內(nèi)核Kernel執(zhí)行neighbour網(wǎng)絡(luò)可達(dá)性檢測(cè)(IPv4 ARP或 IPv6 ND)。
如果網(wǎng)絡(luò)可達(dá)性檢測(cè)請(qǐng)求成功傳輸?shù)絻?nèi)核kernel搀玖,返回0; 其他返回一個(gè)非0的錯(cuò)誤碼余境。
private static int probeNeighbor(int ifIndex, InetAddress ip) {
...
//將ip地址和相應(yīng)的接口號(hào)ifIndex封裝到PROBE消息中
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
//創(chuàng)建NetlinkSocket,成功后進(jìn)行連接kernel, 然后發(fā)送消息灌诅;
最后接收消息芳来,并通過Netlink對(duì)消息進(jìn)行解析。
try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) {
nlSocket.connectToKernel();
nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);
// recvMessage() guaranteed to not return null if it did not throw.
final NetlinkMessage response = NetlinkMessage.parse(bytes);
猜拾。即舌。。//后面依據(jù)不同的response 返回不同的值挎袜。
}}
4顽聂、如果處于NUD_FAILED狀態(tài)的neighbour網(wǎng)絡(luò)的丟失將會(huì)導(dǎo)致Ipv4 或者Ipv6配置變得不完整(配置丟失),我們將會(huì)調(diào)用提供的Callback#notifyLost()函數(shù)盯仪。
private void handleNeighborLost(String msg) {
? ? ? ? LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
??????? for (Map.Entryentry : mIpWatchList.entrySet()) {
???????????????????? //遍歷IpWatchList中的所有neighbor紊搪,尋找value狀態(tài)為NUD_FAILED的
???????????????????? if (entry.getValue() != StructNdMsg.NUD_FAILED) {
?????????????????????????? continue;
????????????????????? }
? ? ? ? ? ? ? ? ? //從IpWatchList找到了value狀態(tài)為NUD_FAILED的neighbor, 獲取其IP地址;
? ? ? ? ? ? ? ? ? //遍歷mLinkProperties中路由Routes的網(wǎng)關(guān)是否有跟該IP地址一致的全景,如果有耀石,則從當(dāng)前的whatIfLp 中移除相應(yīng)的路由Routes。
? ? ? ? ? ? ? ? ? ip = entry.getKey();
????????????????? for (RouteInfo route : mLinkProperties.getRoutes()) {
????????????????? if (ip.equals(route.getGateway())) {
????????????????????????? whatIfLp.removeRoute(route);
???????????????? }
? ? ? ? ? ? ? //如果該IP地址不是IPv6 地址或者使能了避免badLink連接的性能爸黄,則從whatIfLp中移除Ip地址對(duì)應(yīng)的DNS服務(wù)器滞伟。
? ? ? ? ? ? ? ? if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
?????????????????????????? // We should do this unconditionally, but alas we cannot: b/31827713.
?????????????????????????? whatIfLp.removeDnsServer(ip);
??????????????? }
?????????? }
? ? ? ? //調(diào)用LinkProperties的compareProvisioning函數(shù)得到whatIfLp與原始的mLinkProperties的區(qū)別。
? ? ? ? ? delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp);
? ? ? ? ? //如果delta值為ProvisioningChange.LOST_PROVISIONING炕贵,則回調(diào)函數(shù)notifyLost進(jìn)行配置丟失的處理梆奈;該回調(diào)函數(shù)的實(shí)現(xiàn)是在IpManager中,詳見IpManger中的分析称开。
????????? if (delta == ProvisioningChange.LOST_PROVISIONING) {
? ? ? ? ??????????? mCallback.notifyLost(ip, logMsg);
???????? }
}
Android - NetlinkSocketObserver
NetlinkSocketObserver簡介
在IpReachabilityMonitor類中有一個(gè)子類NetlinkSocketObserver類鉴裹。
在IpReachiabilityMonitor 構(gòu)造函數(shù)中會(huì)創(chuàng)建NetlinkSocketObserver對(duì)象,并對(duì)該對(duì)象進(jìn)行封裝钥弯,創(chuàng)建一個(gè)Thread類 對(duì)象径荔,并調(diào)用Thread類對(duì)象的Start方法,啟動(dòng)線程脆霎。
mNetlinkSocketObserver = new NetlinkSocketObserver();
mObserverThread = new Thread(mNetlinkSocketObserver);
mObserverThread.start();
NetlinkSocketObserver作用
NetlinkSocketObserver 類主要和Android Netlink 進(jìn)行交互总处。
Android netlink :frameworks/base/services/net/java/android/net/netlink/...
該類的作用主要是建立NETLINK_ROUTE Socket,綁定相應(yīng)的NetlinkSocketAddress睛蛛。然后無限循環(huán)接收kernelReply信息并對(duì)該消息進(jìn)行解析鹦马。
NetlinkSocketObserver實(shí)現(xiàn)
//通過實(shí)現(xiàn)Runnable接口創(chuàng)建線程胧谈,重寫run方法;
該類的作用就是建立NETLINK_ROUTE Socket荸频,綁定相應(yīng)的NetlinkSocketAddress菱肖。然后無限循環(huán)接收kernelReply信息并對(duì)該消息進(jìn)行解析。
關(guān)鍵接口實(shí)現(xiàn):
Run()
? -->parseNetlinkMessageBuffer(byteBuffer, whenMs)
? ? ? ? -->evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
private final class NetlinkSocketObserver implements Runnable
{
//建立NETLINK_ROUTE Socket旭从,綁定相應(yīng)的NetlinkSocketAddress稳强。然后無限循環(huán)接收kernelReply信息并對(duì)該消息進(jìn)行解析。
public void run() {
//1和悦、建立NETLINK_ROUTE Socket退疫,并綁定bind 相應(yīng)的NetlinkSocketAddress
setupNetlinkSocket();
//2、接收Kernel的Reply信息
byteBuffer = recvKernelReply();
//3鸽素、對(duì)接收到的KernelReply信息進(jìn)行解析
parseNetlinkMessageBuffer(byteBuffer, whenMs);
}
//將ByteBuffer解析為NetlinkMessage格式消息褒繁,并判斷消息類型,調(diào)用評(píng)估消息是否更新相應(yīng)ipadress的IpWatchList馍忽。
private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs)
/*
//1棒坏、通過調(diào)用NetlinkMessage類將ByteBuffer解析為NetlinkMessage格式消息
final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
//2、判斷解析出現(xiàn)的NetlinkMessage消息類型遭笋,確保是RtNetlinkNeighborMessage消息才繼續(xù)進(jìn)行評(píng)估俊抵;如果是NetlinkErrorMessage或不是RtNetlinkNeighborMessage消息直接退出。
nlMsg instanceof NetlinkErrorMessage
nlMsg instanceof RtNetlinkNeighborMessage?
//3坐梯、調(diào)用evaluateRtNetlinkNeighborMessage對(duì)RtNetlinkNeighborMessage消息進(jìn)行評(píng)估徽诲。
evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
*/
//依據(jù)從RtNetlinkNeighborMessage獲取的msgType和nudState,對(duì)IpWatchList中相應(yīng)的InetAddress的nudState 進(jìn)行更新吵血。
private void evaluateRtNetlinkNeighborMessage
(RtNetlinkNeighborMessage neighMsg, long whenMs)
/*
//1谎替、從neighMsg取目的IP地址,并判斷該IP地址是否在IpWatchList中蹋辅,不在則直接退出函數(shù)返回
final InetAddress destination = neighMsg.getDestination();
//2钱贯、從neighMsg取msgType和nudState,并根據(jù)他們進(jìn)行IpWatchList 的更新
final short msgType = neighMsg.getHeader().nlmsg_type;
final short nudState = ndMsg.ndm_state;
final short value =
(msgType == NetlinkConstants.RTM_DELNEIGH) ? StructNdMsg.NUD_NONE: nudState;
mIpWatchList.put(destination, value);
//3侦另、如果nudState為NUD_FAILED狀態(tài)秩命,則執(zhí)行handleNeighborLost動(dòng)作
if (nudState == StructNdMsg.NUD_FAILED) {
handleNeighborLost(eventMsg);? //這個(gè)詳見IpReachiabilityMonitor的實(shí)現(xiàn)方式的第4部分。
*/
}