什么是NSD土全?
NSD全稱(chēng)為: Network Service Discovery.也就是網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)的意思捎琐。(可以在局域網(wǎng)內(nèi)發(fā)現(xiàn)同樣使用nsd注冊(cè)了的應(yīng)用設(shè)備的網(wǎng)絡(luò)信息)
NSD應(yīng)用于哪?
通常應(yīng)用于局域網(wǎng)內(nèi)不同應(yīng)用設(shè)備的互聯(lián)
- 發(fā)現(xiàn)配置打印機(jī)
- 應(yīng)用小游戲等的互聯(lián)
NSD簡(jiǎn)介
NSD(NsdManager)是Android SDK中自帶的類(lèi)庫(kù)裹匙,可以集成直接使用瑞凑。
使用 NSD服務(wù)需要(android4.1及以上) minSdkVersion >16
NSD主要包含兩個(gè)功能:
1. NSD 注冊(cè)功能:
進(jìn)行NSD注冊(cè):自定義服務(wù)名、端口號(hào)概页,IP地址注冊(cè)到NSD服務(wù)中
2. NSD 掃描功能:
掃描到當(dāng)前局域網(wǎng)內(nèi)所有已通過(guò)NSD注冊(cè)了的應(yīng)用設(shè)備的網(wǎng)絡(luò)信息(服務(wù)名籽御、端口號(hào)、IP地址)
NSD基本原理
實(shí)現(xiàn)了網(wǎng)絡(luò)發(fā)現(xiàn)服務(wù)NsdService惰匙,其基于蘋(píng)果的Bonjour服務(wù)發(fā)現(xiàn)協(xié)議
Bonjour協(xié)議主要包括:
- 服務(wù)的發(fā)現(xiàn)
- 服務(wù)名稱(chēng)與地址的轉(zhuǎn)換
Bonjour協(xié)議流程和DNS流程近似技掏,包括:
1. 服務(wù)登記過(guò)程
2. 服務(wù)發(fā)現(xiàn)過(guò)程
3. 服務(wù)地址解析過(guò)程
4. 建立連接等過(guò)程
服務(wù)發(fā)現(xiàn)采用的協(xié)議也和DNS類(lèi)似,不過(guò)與DNS協(xié)議采用的單播方式不同的是采用了組播方式项鬼,因此被稱(chēng)為mDNS哑梳。
什么是mDNS?
mDNS multicast DNS (組播Dns)
首先绘盟,在 IP 協(xié)議里規(guī)定了一些保留地址鸠真,其中有一個(gè)是 224.0.0.251,對(duì)應(yīng)的 IPv6 地址是 [FF02::FB]龄毡。
mDNS 協(xié)議規(guī)定了一個(gè)端口吠卷,5353。
mDNS 基于 UDP 協(xié)議沦零。
mDNS注冊(cè)掃描流程:A主機(jī)進(jìn)入局域網(wǎng)祭隔,開(kāi)啟了 mDNS 服務(wù),并向 mDNS 服務(wù)注冊(cè)以下信息:我的服務(wù)名是AiXue,我的IP是 192.168.1.101路操,端口是 21序攘。當(dāng)B主機(jī)進(jìn)入局域網(wǎng),并向 mDNS 服務(wù)進(jìn)行掃描請(qǐng)求寻拂,掃描到mDNS服務(wù)中所有已注冊(cè)的主機(jī)后程奠,從中過(guò)濾出服務(wù)名是AiXue的主機(jī),并解析獲得到它的網(wǎng)絡(luò)信息為IP地址為 192.168.1.101祭钉,端口號(hào)是 21 瞄沙。
ANDROID借助第三方開(kāi)源工程mDNSResponder實(shí)現(xiàn)了Bonjour協(xié)議。
ANDROID對(duì)網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)的實(shí)現(xiàn)架構(gòu)包括四層:
- NSD應(yīng)用層
- 服務(wù)發(fā)現(xiàn)服務(wù)框架層(對(duì)應(yīng)NsdService)
- MDns后臺(tái)監(jiān)聽(tīng)層(對(duì)應(yīng)運(yùn)行在netd本地服務(wù)進(jìn)程的MDnsSdListener類(lèi) )
- MDns后臺(tái)服務(wù)(對(duì)應(yīng)mdnsd本地服務(wù)進(jìn)程)慌核。
架構(gòu)的每層作為其上一層的服務(wù)端對(duì)上一層提供服務(wù)距境,四層分別運(yùn)行在不同的進(jìn)程,采用相應(yīng)的跨進(jìn)程通訊方式進(jìn)行交互,上層通過(guò)connect與下層服務(wù)建立連接垮卓。其中NsdService 和NSD應(yīng)用層采用JAVA語(yǔ)言實(shí)現(xiàn) 垫桂,MDns后臺(tái)監(jiān)視采用C++實(shí)現(xiàn),而MDns后臺(tái)服務(wù)為采用C語(yǔ)言的開(kāi)源代碼粟按。
NSD應(yīng)用層通過(guò)NsdService層提供的NsdManager類(lèi)诬滩,對(duì)NSD進(jìn)行注冊(cè)霹粥、掃描、接收響應(yīng)等操作疼鸟。
NsdService處于整個(gè)層次的承上啟下層后控,其通過(guò)NsdManager對(duì)應(yīng)用層提供調(diào)用和回調(diào)服務(wù),NsdManager和NsdService服務(wù)之間采用AsyncChannel異步通道進(jìn)行消息交互空镜。NsdService服務(wù)對(duì)下在其N(xiāo)ativeDaemonConnector線程對(duì)象中使用UNIX SOCKET接口與MDnsSdListener建立跨進(jìn)程連接浩淘。
在MDnsSdListener類(lèi)中調(diào)用mDNSResponder開(kāi)源工程提供的客戶端樁接口與MDns后臺(tái)服務(wù)建立本地SOCKET通訊,并采用Monitor對(duì)象來(lái)啟動(dòng)MDns后臺(tái)服務(wù)吴攒,實(shí)現(xiàn)MDns后臺(tái)服務(wù)的事件監(jiān)聽(tīng)和事件回調(diào)處理等工作张抄。MDnsSdListener及Monitor對(duì)象與MDns后臺(tái)服務(wù)的交互也是采用UNIX SOCKET機(jī)制進(jìn)行跨進(jìn)程交互。
MDns后臺(tái)服務(wù)的整個(gè)實(shí)現(xiàn)代碼及客戶端的樁實(shí)現(xiàn)由第三方工程mDNSResponder提供洼怔,代碼位于 external目錄下 的mdnsresponder中欣鳖,包括mDNSCore(包括MDNS核心協(xié)議引擎代碼)、mDNSShared多個(gè)平臺(tái)共享的非核心引擎代碼茴厉、mDNSPosix Posix平臺(tái)相關(guān)代碼泽台、Clients包括如何使用后臺(tái)服務(wù)提供的API的客戶端例子代碼等四個(gè)目錄,整個(gè)工程編譯生成一個(gè)mdnsd后臺(tái)服務(wù)和一個(gè)MDns監(jiān)視層使用的庫(kù)libmdnssd矾缓,而Clients中的代碼生成一個(gè)dnssd執(zhí)行文件用于測(cè)試怀酷。
NSD 注冊(cè)功能開(kāi)發(fā)
1.注冊(cè)NSD
private NsdManager mNsdManager;
//NSD注冊(cè)
private void registerService(Context context, String serviceName, int port) {
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName("AiXue");
serviceInfo.setPort(21);
serviceInfo.setServiceType("_http._tcp.");//掃描是需要對(duì)應(yīng)的這個(gè)Type字符串
mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}
到這里如果其他設(shè)備馬上進(jìn)行掃描就能看到注冊(cè)了NSD的服務(wù)器網(wǎng)絡(luò)信息
2.注銷(xiāo)NSD
public void stopNSDServer() {
mNsdManager.unregisterService(mRegistrationListener);
}
可以取消掉注冊(cè)NSD服務(wù)器,就是讓別人掃描不到你的NSD服務(wù)器
3.注冊(cè)監(jiān)聽(tīng)器
private NsdManager.RegistrationListener mRegistrationListener;
//實(shí)例化注冊(cè)監(jiān)聽(tīng)器
private void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
@Override
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceRegistered: " + serviceInfo);
}
@Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "NsdServiceInfo onRegistrationFailed");
}
@Override
public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceUnregistered serviceInfo: " + serviceInfo);
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.i(TAG, "onUnregistrationFailed serviceInfo: " + serviceInfo + " ,errorCode:" + errorCode);
}
};
}
NSD掃描功能開(kāi)發(fā)
開(kāi)始NSD掃描
private var mNsdManager: NsdManager? = null
/**
* 啟動(dòng)nsd掃描
* @param mServiceName 服務(wù)名與注冊(cè)者保持一致
* @param mIDiscoverState 掃描狀態(tài)回調(diào)
*/
fun startNsdClient() {
mNsdManager = mContext.getSystemService(Context.NSD_SERVICE) as NsdManager
mNsdManager?.discoverServices("_http._tcp.", NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener)
}
進(jìn)行發(fā)現(xiàn)服務(wù)操作后嗜闻,會(huì)在掃描監(jiān)聽(tīng)器對(duì)應(yīng)的方法得到數(shù)據(jù)蜕依。
停止NSD掃描
fun stopNsdServer() {
mNsdManager?.stopServiceDiscovery(mDiscoveryListener)
}
注冊(cè)掃描監(jiān)聽(tīng)器
private fun initializeDiscoveryListener() {
mDiscoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(serviceType: String) {}
override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
mNsdManager?.stopServiceDiscovery(this)
}
override fun onDiscoveryStopped(serviceType: String) {}
override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
mNsdManager?.stopServiceDiscovery(this)
}
override fun onServiceFound(serviceInfo: NsdServiceInfo) {
if (serviceInfo.serviceType == "_http._tcp." && serviceInfo.serviceName == "AiXue") {
// 解析
mNsdManager?.resolveService(serviceInfo, object : NsdManager.ResolveListener {
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {"onResolveFailed")
}
override fun onServiceResolved(serviceInfo: NsdServiceInfo) {
val port = serviceInfo.port
val host = serviceInfo.host
}
})
}
}
override fun onServiceLost(serviceInfo: NsdServiceInfo) {
LiveLocalLog.e("onServiceLost: serviceInfo=$serviceInfo")
mIDiscoverState?.onDiscoverFail(100, "onServiceLost")
}
}
}
注冊(cè)流程源碼分析
-
NsdManager的實(shí)例化
應(yīng)用通過(guò)調(diào)用Context.getSystemService(Context.NSD_SERVICE)獲得NsdManager的實(shí)例。在NsdManager的實(shí)例化過(guò)程中對(duì)使用到的資源進(jìn)行實(shí)例化琉雳,包括調(diào)用NsdService的getMessenger函數(shù)獲得服務(wù)的Messenger對(duì)象用作客戶端消息的發(fā)送目標(biāo)样眠,實(shí)例化和啟動(dòng)事件處理線程HandlerThread及實(shí)例化事件接收處理對(duì)象ServiceHandler,AsyncChannel對(duì)象的實(shí)例化并且調(diào)用AsyncChannel對(duì)象的connec函數(shù)與NsdService建立連接翠肘。
在NsdService服務(wù)接收到連接消息后檐束,實(shí)例化一個(gè)服務(wù)端的AsyncChannel對(duì)象,并根據(jù)消息的源和服務(wù)端的AsyncChannel對(duì)象實(shí)例化一個(gè)ClientInfo對(duì)象放入mClients HashMap數(shù)組中束倍。
public NsdManager(Context context, INsdManager service) {
mService = service;
mContext = context;
init();
}
private void init() {
final Messenger messenger = getMessenger();
if (messenger == null) {
fatal("Failed to obtain service Messenger");
}
HandlerThread t = new HandlerThread("NsdManager");
t.start();
mHandler = new ServiceHandler(t.getLooper());
mAsyncChannel.connect(mContext, mHandler, messenger);
try {
mConnected.await();
} catch (InterruptedException e) {
fatal("Interrupted wait at init");
}
}
-
NsdManager的注冊(cè)
應(yīng)用調(diào)用NsdManager實(shí)例的registerService接口被丧,registerService接口參數(shù)中包含一個(gè)NsdServiceInfo參數(shù)(指示要登記的服務(wù)信息)、一個(gè)protocolType參數(shù)(指定協(xié)議類(lèi)型)以及一個(gè)監(jiān)聽(tīng)對(duì)象listener绪妹,用來(lái)接收響應(yīng)事件回調(diào)甥桂。
在registerService接口中調(diào)用putListener函數(shù)分別把NsdServiceInfo參數(shù)和監(jiān)聽(tīng)對(duì)象listener保存到mServiceMap和mListenerMap的映射數(shù)組中,并返回?cái)?shù)組的鍵值key邮旷;然后registerService通過(guò)NsdManager的AsyncChannel對(duì)象向目標(biāo)發(fā)送REGISTER_SERVICE消息黄选,發(fā)送的消息參數(shù)包括putListener函數(shù)返回的key以及NsdServiceInfo信息。
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
RegistrationListener listener) {
checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
}
private int putListener(Object listener, NsdServiceInfo s) {
checkListener(listener);
final int key;
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
checkArgument(valueIndex == -1, "listener already in use");
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
}
return key;
}
-
NsdService注冊(cè)及監(jiān)聽(tīng)
NsdService服務(wù)收到REGISTER_SERVICE消息后婶肩,首先根據(jù)消息源從mClients數(shù)組中獲得clientInfo對(duì)象办陷,然后調(diào)用getUniqueId獲得一個(gè)UniqueId作為登記請(qǐng)求ID貌夕;接著調(diào)用服務(wù)端的registerService函數(shù),registerService的參數(shù)為UniqueId和消息傳進(jìn)來(lái)的NsdServiceInfo信息懂诗。
case NsdManager.REGISTER_SERVICE:
if (DBG) Slog.d(TAG, "Register service");
clientInfo = mClients.get(msg.replyTo);
if (requestLimitReached(clientInfo)) {
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.FAILURE_MAX_LIMIT);
break;
}
id = getUniqueId();
if (registerService(id, (NsdServiceInfo) msg.obj)) {
if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id);
storeRequestMap(msg.arg2, id, clientInfo, msg.what);
// Return success after mDns reports success
} else {
unregisterService(id);
replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED,
NsdManager.FAILURE_INTERNAL_ERROR);
}
break;
NsdService的服務(wù)注冊(cè):在registerService函數(shù)中調(diào)用NativeDaemonConnector對(duì)象的execute函數(shù)蜂嗽,execute函數(shù)的命令參數(shù)為”mdnssd”苗膝,其它參數(shù)包括登記命令名稱(chēng)標(biāo)示"register"殃恒、登記ID、從NsdServiceInfo中獲得的ServiceName辱揭、ServiceType和port等參數(shù)离唐。
NativeDaemonConnector對(duì)象在NsdService服務(wù)實(shí)例化時(shí)實(shí)例化, NativeDaemonConnector對(duì)象實(shí)例化mSocket參數(shù)為"mdns"问窃,mCallbacks參數(shù)指向NsdService服務(wù)內(nèi)部NativeCallbackReceiver對(duì)象亥鬓。NativeDaemonConnector對(duì)象本身是一個(gè)派生自Runnable的線程對(duì)象,因此其線程函數(shù)run也在實(shí)例化后啟動(dòng)域庇。
private boolean registerService(int regId, NsdServiceInfo service) {
if (DBG) {
Slog.d(TAG, "registerService: " + regId + " " + service);
}
String name = service.getServiceName();
String type = service.getServiceType();
int port = service.getPort();
byte[] textRecord = service.getTxtRecord();
String record = Base64.encodeToString(textRecord, Base64.DEFAULT).replace("\n", "");
return mDaemon.execute("register", regId, name, type, port, record);
}
public static NsdService create(Context context) throws InterruptedException {
NsdSettings settings = NsdSettings.makeDefault(context);
HandlerThread thread = new HandlerThread(TAG);
thread.start();
Handler handler = new Handler(thread.getLooper());
NsdService service = new NsdService(context, settings, handler, DaemonConnection::new);
service.mDaemonCallback.awaitConnection();
return service;
}
public static class DaemonConnection {
final NativeDaemonConnector mNativeConnector;
DaemonConnection(NativeCallbackReceiver callback) {
mNativeConnector = new NativeDaemonConnector(callback, "mdns", 10, MDNS_TAG, 25, null);
new Thread(mNativeConnector, MDNS_TAG).start();
}
public boolean execute(Object... args) {
if (DBG) {
Slog.d(TAG, "mdnssd " + Arrays.toString(args));
}
try {
mNativeConnector.execute("mdnssd", args);
} catch (NativeDaemonConnectorException e) {
Slog.e(TAG, "Failed to execute mdnssd " + Arrays.toString(args), e);
return false;
}
return true;
}
}
Socket讀操作來(lái)監(jiān)聽(tīng)mDnsListener層發(fā)來(lái)的消息:listenToSocket首先實(shí)例化一個(gè)本地socket對(duì)象嵌戈,LocalSocket對(duì)象的LocalSocketAddress地址的 Socket名稱(chēng)為已初始化的mSocket,并使用該地址調(diào)用connect函數(shù)听皿,從init.rc 可以看到名稱(chēng)為"mdns"的Socket對(duì)應(yīng)的本地服務(wù)為netd熟呛,因此NativeCallbackReceiver對(duì)象與netd服務(wù)建立了連接;然后listenToSocket函數(shù)調(diào)用socket的getInputStream和getOutputStream函數(shù)獲得輸入和輸出流對(duì)象尉姨;最后listenToSocket函數(shù)進(jìn)入while循環(huán)不斷從輸入流讀取事件進(jìn)行分析庵朝。解析后的事件發(fā)給HandlerThread線程的Handler函數(shù)進(jìn)行處理,在Handler函數(shù)中調(diào)用mCallbacks的onEvent回調(diào)函數(shù)又厉,即NsdService服務(wù)內(nèi)部NativeCallbackReceiver對(duì)象的onEvent回調(diào)函數(shù)九府。
@Override
public void run() {
mCallbackHandler = new Handler(mLooper, this);
while (true) {
if (isShuttingDown()) break;
try {
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
if (isShuttingDown()) break;
SystemClock.sleep(5000);
}
}
}
private void listenToSocket() throws IOException {
LocalSocket socket = null;
try {
socket = new LocalSocket();
LocalSocketAddress address = determineSocketAddress();
socket.connect(address);
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
mCallbacks.onDaemonConnected();
FileDescriptor[] fdList = null;
byte[] buffer = new byte[BUFFER_SIZE];
int start = 0;
while (true) {
int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
if (count < 0) {
loge("got " + count + " reading with start = " + start);
break;
}
fdList = socket.getAncillaryFileDescriptors();
// Add our starting point to the count and reset the start.
count += start;
start = 0;
for (int i = 0; i < count; i++) {
if (buffer[i] == 0) {
// Note - do not log this raw message since it may contain
// sensitive data
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);
boolean releaseWl = false;
try {
final NativeDaemonEvent event =
NativeDaemonEvent.parseRawEvent(rawEvent, fdList);
log("RCV <- {" + event + "}");
if (event.isClassUnsolicited()) {
// TODO: migrate to sending NativeDaemonEvent instances
if (mCallbacks.onCheckHoldWakeLock(event.getCode())
&& mWakeLock != null) {
mWakeLock.acquire();
releaseWl = true;
}
Message msg = mCallbackHandler.obtainMessage(
event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());
if (mCallbackHandler.sendMessage(msg)) {
releaseWl = false;
}
} else {
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
log("Problem parsing message " + e);
} finally {
if (releaseWl) {
mWakeLock.release();
}
}
start = i + 1;
}
}
if (start == 0) {
log("RCV incomplete");
}
// We should end at the amount we read. If not, compact then
// buffer and read again.
if (start != count) {
final int remaining = BUFFER_SIZE - start;
System.arraycopy(buffer, start, buffer, 0, remaining);
start = remaining;
} else {
start = 0;
}
}
} catch (IOException ex) {
loge("Communications error: " + ex);
throw ex;
} finally {
synchronized (mDaemonLock) {
if (mOutputStream != null) {
try {
loge("closing stream for " + mSocket);
mOutputStream.close();
} catch (IOException e) {
loge("Failed closing output stream: " + e);
}
mOutputStream = null;
}
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException ex) {
loge("Failed closing socket: " + ex);
}
}
}
-
NsdServer通過(guò)Socket往mDnsListener層寫(xiě)操作
在NativeDaemonConnector對(duì)象的execute函數(shù)中首先根據(jù)傳進(jìn)的參數(shù)調(diào)用makeCommand函數(shù)生成一個(gè)字符串類(lèi)型的命令,然后調(diào)用本地socket的輸出流對(duì)象 mOutputStream的write函數(shù)來(lái)發(fā)送命令覆致。
public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
throws NativeDaemonConnectorException {
...
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
final String rawCmd = rawBuilder.toString();
final String logCmd = logBuilder.toString();
log("SND -> {" + logCmd + "}");
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw new NativeDaemonConnectorException("missing output stream");
} else {
try {
mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new NativeDaemonConnectorException("problem sending command", e);
}
}
}
...
}
static void makeCommand(StringBuilder rawBuilder, StringBuilder logBuilder, int sequenceNumber,
String cmd, Object... args) {
if (cmd.indexOf('\0') >= 0) {
throw new IllegalArgumentException("Unexpected command: " + cmd);
}
if (cmd.indexOf(' ') >= 0) {
throw new IllegalArgumentException("Arguments must be separate from command");
}
rawBuilder.append(sequenceNumber).append(' ').append(cmd);
logBuilder.append(sequenceNumber).append(' ').append(cmd);
for (Object arg : args) {
final String argString = String.valueOf(arg);
if (argString.indexOf('\0') >= 0) {
throw new IllegalArgumentException("Unexpected argument: " + arg);
}
rawBuilder.append(' ');
logBuilder.append(' ');
appendEscaped(rawBuilder, argString);
if (arg instanceof SensitiveArg) {
logBuilder.append("[scrubbed]");
} else {
appendEscaped(logBuilder, argString);
}
}
rawBuilder.append('\0');
}
在本地服務(wù)netd的進(jìn)程中調(diào)用其MDnsSdListener對(duì)象的startListener函數(shù)啟動(dòng)命令的監(jiān)聽(tīng)侄旬。
MDnsSdListener對(duì)象通過(guò)FrameworkListener間接派生自SocketListener,在MDnsSdListener對(duì)象實(shí)例化時(shí)其成員mSocketName初始化 為"mdns"煌妈,因此對(duì)應(yīng)的socket通道和NativeCallbackReceiver對(duì)象中的socket通道相同勾怒。MDnsSdListener實(shí)例化時(shí)還初始化一個(gè)Monitor對(duì)象和一個(gè)FrameworkCommand類(lèi)型的Handler對(duì)象。
Handler對(duì)象初始化時(shí)其mCommand屬性賦值為"mdnssd"声旺,用來(lái)和發(fā)送來(lái)的命令匹配笔链,Handler對(duì)象也保存到FrameworkCommand命令列表對(duì)象中mCommands。
Monitor對(duì)象實(shí)例化時(shí)調(diào)用socketpair函數(shù)建立一個(gè)Socket組mCtrlSocketPair腮猖,還創(chuàng)建一個(gè)監(jiān)聽(tīng)線程鉴扫,線程中調(diào)用Monitor對(duì)象的run函數(shù)。startListener函數(shù)首先調(diào)用android_get_control_socket函數(shù)根據(jù)mSocketName名稱(chēng)獲得其SOCKET fd澈缺;然后調(diào)用listen函數(shù)監(jiān)聽(tīng)socket通道坪创;
然后創(chuàng)建一個(gè)線程炕婶,在線程中執(zhí)行runListener函數(shù),在runListener函數(shù)循環(huán)調(diào)用accept接收客戶端連接莱预。當(dāng)有客戶端連接后柠掂,根據(jù)accept返回的socket fd實(shí)例化一個(gè)SocketClient對(duì)象保存到SocketClient對(duì)象列表中mClients,并調(diào)用onDataAvailable函數(shù)依沮。
onDataAvailable函數(shù)調(diào)用read函數(shù)讀取客戶端發(fā)送的命令涯贞,并調(diào)用dispatchCommand函數(shù)提交命令。
在dispatchCommand函數(shù)中解析命令參數(shù)危喉,并與mCommands命令對(duì)象列表進(jìn)行命令匹配宋渔,并調(diào)用匹配后命令對(duì)象的runCommand函數(shù),這里即調(diào)用MDnsSdListener對(duì)象中的Handler對(duì)象的runCommand函數(shù)辜限。在Handler對(duì)象的runCommand函數(shù)中進(jìn)行命令參數(shù)的匹配皇拣,這里匹配的是"register",因此在獲得命令參數(shù)后調(diào)用serviceRegister函數(shù)薄嫡,serviceRegister函數(shù)參數(shù)包括匹配的SocketClient對(duì)象以及命令參數(shù)信息氧急。
在serviceRegister函數(shù)中,首先調(diào)用mMonitor的allocateServiceRef函數(shù)根據(jù)請(qǐng)求ID實(shí)例化一個(gè)Element對(duì)象放入鏈表中毫深,并返回Element對(duì)象的DNSServiceRef指針吩坝,DNSServiceRef指向_DNSServiceRef_t結(jié)構(gòu),其成員包括DNS操作或應(yīng)答類(lèi)型费什,接收消息回調(diào)接口钾恢、客戶端回調(diào)和上下文、客戶端與服務(wù)端連接socket等參數(shù)鸳址。
然后調(diào)用DNSServiceRegister函數(shù)瘩蚪,DNSServiceRegister函數(shù)用來(lái)向本地MDns后臺(tái)服務(wù)發(fā)起連接和消息請(qǐng)求,DNSServiceRegister函數(shù)的參數(shù)包括allocateServiceRef函數(shù)返回的DNSServiceRef指針變量以及serviceRegister傳進(jìn)來(lái)的命令請(qǐng)求參數(shù)稿黍,以及事件接收回調(diào)函數(shù)MDnsSdListenerRegisterCallback疹瘦。DNSServiceRegister函數(shù)為mDNSResponder開(kāi)源工程提供的客戶端調(diào)用API接口,用來(lái)與MDns后臺(tái)服務(wù)建立連接巡球,并向其提交請(qǐng)求言沐。
在DNSServiceRegister函數(shù)中首先通過(guò)ConnectToServer函數(shù)與MDns后臺(tái)服務(wù)建立連接。
在ConnectToServer函數(shù)首先實(shí)例和初始化一個(gè)_DNSServiceRef_t類(lèi)型DNSServiceOp變量酣栈,然后創(chuàng)建一個(gè)本地socket险胰,且新建socket的文件句柄賦值給DNSServiceOp對(duì)象的sockfd。
然后調(diào)用connect與MDns后臺(tái)服務(wù)建立連接矿筝,最后把實(shí)例化后的DNSServiceOp對(duì)象通過(guò)DNSServiceRef參數(shù)帶回起便。
ConnectToServer函數(shù)返回后接著調(diào)用create_hdr函數(shù)為實(shí)例化一個(gè)ipc_msg_hdr類(lèi)型的請(qǐng)求消息,并對(duì)請(qǐng)求消息賦值后連同ConnectToServer函數(shù)帶回的DNSServiceRef參數(shù)一同傳給deliver_request函數(shù),通過(guò)deliver_request函數(shù)提交請(qǐng)求榆综。在Monitor對(duì)象的run函數(shù)中循環(huán)對(duì)mPollFds進(jìn)行poll操作妙痹。
在startMonitoring函數(shù)通過(guò)向mCtrlSocketPair[1]寫(xiě)入RESCAN命令后,由于mPollFds[0].fd指向mCtrlSocketPair[0]鼻疮,因此mMonitor的run函數(shù)在mPollFds[0]通道讀取到RESCAN命令并調(diào)用RESCAN函數(shù)怯伊,在RESCAN函數(shù)中根據(jù)已建立的與服務(wù)器的連接為mPollFds的其它通道賦值,這些mPollFds通道的文件句柄位賦值為服務(wù)器已建立連接的socket 的句柄判沟。
在服務(wù)端的響應(yīng)事件到來(lái)時(shí)在這些通道poll到事件耿芹,然后調(diào)用DNSServiceProcessResult函數(shù),參數(shù)為DNSServiceRef水评。在DNSServiceProcessResult函數(shù)中讀取響應(yīng)事件和數(shù)據(jù)猩系,并調(diào)用DNSServiceRef參數(shù)的事件回調(diào)ProcessReply函數(shù)媚送,即對(duì)于服務(wù)登記請(qǐng)求對(duì)應(yīng)的是MDnsSdListenerRegisterCallback函數(shù)中燥。
在MDnsSdListenerRegisterCallback中向Handler對(duì)象的監(jiān)聽(tīng)對(duì)象的sendBroadcast函數(shù)發(fā)送ResponseCode::ServiceRegistrationSucceeded應(yīng)答消息,Handler對(duì)象的監(jiān)聽(tīng)對(duì)象為MDnsSdListener對(duì)象本身塘偎,因此這里調(diào)用SocketListener的sendBroadcast函數(shù)求厕。
在sendBroadcast函數(shù)中遍歷mClients對(duì)象的成員對(duì)象须床,并調(diào)用其調(diào)用sendMsg函數(shù),即調(diào)用SocketClient的sendMsg函數(shù)。
在sendMsg函數(shù)中通過(guò)與客戶端(即NsdService服務(wù)的NativeDaemonConnector對(duì)象)建立的SOCKET向客戶端發(fā)送應(yīng)答消息肌毅。在NsdService的NativeDaemonConnector對(duì)象的listenToSocket函數(shù) 收到服務(wù)端的應(yīng)答消息后,調(diào)用NsdService服務(wù)內(nèi)部NativeCallbackReceiver對(duì)象的onEvent回調(diào)函數(shù)俄周。
在onEvent回調(diào)函數(shù)中向NsdService服務(wù)的狀態(tài)機(jī)發(fā)送NsdManager.NATIVE_DAEMON_EVENT事件亡电,假如這時(shí)NsdService服務(wù)處于EnabledState狀態(tài),狀態(tài)機(jī)收到NsdManager.NATIVE_DAEMON_EVENT事件后調(diào)用handleNativeEvent函數(shù)壮池。
class NativeCallbackReceiver implements INativeDaemonConnectorCallbacks {
...
@Override
public boolean onEvent(int code, String raw, String[] cooked) {
// TODO: NDC translates a message to a callback, we could enhance NDC to
// directly interact with a state machine through messages
NativeEvent event = new NativeEvent(code, raw, cooked);
mNsdStateMachine.sendMessage(NsdManager.NATIVE_DAEMON_EVENT, event);
return true;
}
}
handleNativeEvent函數(shù)首先根據(jù)響應(yīng)消息的請(qǐng)求ID從mIdToClientInfoMap中獲得先前應(yīng)用層建立連接時(shí)保存的clientInfo對(duì)象及從clientInfo對(duì)象獲得clientId偏瓤,然后執(zhí)行響應(yīng)事件代碼為NativeResponseCode.SERVICE_REGISTERED的事件處理,事件處理先根據(jù)返回的響應(yīng)事件實(shí)例化一個(gè)NsdServiceInfo對(duì)象椰憋,然后通過(guò)clientInfo中的AsyncChannel對(duì)象成員向NsdService服務(wù)的應(yīng)用層發(fā)送NsdManager.REGISTER_SERVICE_SUCCEEDED響應(yīng)事件厅克。
private boolean handleNativeEvent(int code, String raw, String[] cooked) {
NsdServiceInfo servInfo;
int id = Integer.parseInt(cooked[1]);
ClientInfo clientInfo = mIdToClientInfoMap.get(id);
if (clientInfo == null) {
String name = NativeResponseCode.nameOf(code);
Slog.e(TAG, String.format("id %d for %s has no client mapping", id, name));
return false;
}
/* This goes in response as msg.arg2 */
int clientId = clientInfo.getClientId(id);
if (clientId < 0) {
// This can happen because of race conditions. For example,
// SERVICE_FOUND may race with STOP_SERVICE_DISCOVERY,
// and we may get in this situation.
String name = NativeResponseCode.nameOf(code);
Slog.d(TAG, String.format(
"Notification %s for listener id %d that is no longer active",
name, id));
return false;
}
if (DBG) {
String name = NativeResponseCode.nameOf(code);
Slog.d(TAG, String.format("Native daemon message %s: %s", name, raw));
}
switch (code) {
...
case NativeResponseCode.SERVICE_REGISTERED:
/* NNN regId serviceName regType */
servInfo = new NsdServiceInfo(cooked[2], null);
clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED,
id, clientId, servInfo);
break;
...
}
return true;
}
- NsdManager的事件接收對(duì)象ServiceHandler接收到NsdManager.REGISTER_SERVICE_SUCCEEDED響應(yīng)事件,在其handleMessage函數(shù)中調(diào)用其監(jiān)聽(tīng)對(duì)象(NSD應(yīng)用層)的onServiceRegistered回調(diào)橙依。到此整個(gè)服務(wù)登記流程結(jié)束证舟。