自android 4.1 开始实现了一个网络服务的发现服务NsdService,其基于苹果的Bonjour服务发现协议,支持远程服务的发现和零配置。

Bonjour协议包括IP地址的自动分配、服务名称与地址的转换以及服务的发现三部分内容,ANDROID4.1借助第三方开源工程mDNSResponder实现了Bonjour协议的服务名称与地址的转换以及服务的发现等 Bonjour部分协议的支持。Bonjour协议的服务名称与地址的转换以及服务的发现采用的流程和DNS流程近似包括:登记过程、服务发现过程、服务地址解析过程以及建立连接等过程,服务发现采用的协议也和DNS协议相似,不过与DNS协议采用的单播方式不同的是采用了组播方式,因此被称为mDNS。

ANDROID4.2 对网络服务发现的实现架构包括四层:NSD应用客户端、服务发现服务框架层(对应NsdService)、MDns后台监视层(对应运行在netd本地服务进程的MDnsSdListener类 )以及MDns后台服务(对应mdnsd本地服务进程)。架构的每层作为其上一层的服务端对上一层提供服务,上层通过connect与下层服务建立连接。其中NsdService 和NSD应用客户端采用JAVA语言实现 ,MDns后台监视采用C++实现,而MDns后台服务为采用C语言的开源代码。四层分别运行在不同的进程,采用相应的跨进程通讯方式进行交互。

NsdService处于整个层次的承上启下层,其通过NsdManager对上层应用客户端提供调用和回调服务,NsdManager客户和NsdService服务之间采用AsyncChannel异步通道进行消息交互。NsdService服务对下在其NativeDaemonConnector线程对象中使用UNIX SOCKET接口与MDns后台监视层建立跨进程连接,传输命令和接收响应,MDns后台监视层的MDnsSdListener对象运行在netd本地服务中。

在MDnsSdListener类中调用mDNSResponder开源工程提供的客户端桩接口与MDns后台服务建立本地SOCKET通讯,并采用Monitor对象来启动MDns后台服务,实现MDns后台服务的事件监听和事件回调处理等工作。MDnsSdListener及Monitor对象与MDns后台服务的交互也是采用UNIX SOCKET机制进行跨进程交互。

MDns后台服务的整个实现代码及客户端的桩实现由第三方工程mDNSResponder提供,代码位于 external目录下 的mdnsresponder中,包括mDNSCore(包括MDNS核心协议引擎代码)、mDNSShared多个平台共享的非核心引擎代码、mDNSPosix  Posix平台相关代码、Clients包括如何使用后台服务提供的API的客户端例子代码等四个目录,整个工程编译生成一个mdnsd后台服务和一个MDns监视层使用的库libmdnssd,而Clients中的代码生成一个dnssd执行文件用于测试。

一个应用为了让网络上的其它应用发现它需要通过网络声明自己,即服务登记,这通过调用NsdManager的registerService接口实现。

下面分步骤描述服务登记流程。

1、应用通过调用Context.getSystemService(Context.NSD_SERVICE)获得NsdManager的实例。

在NsdManager的实例化过程中对使用到的资源进行实例化,包括调用NsdService的getMessenger函数获得服务的Messenger对象用作客户端消息的发送目标,实例化和启动事件处理线程HandlerThread及实例化事件接收处理对象ServiceHandler,AsyncChannel对象的实例化并且调用AsyncChannel对象的connect函数与目标建立连接。

在NsdService服务接收到连接消息后,实例化一个服务端的AsyncChannel对象,并根据消息的源和服务端的AsyncChannel对象实例化一个ClientInfo对象放入mClients HashMap数组中。

2、应用调用NsdManager实例的registerService接口,registerService接口参数中包含一个NsdServiceInfo参数(指示要登记的服务信息)、一个protocolType参数(指定协议类型)以及一个监听对象listener,用来接收响应事件回调。

在registerService接口中调用putListener函数分别把NsdServiceInfo参数和监听对象listener保存到mServiceMap和mListenerMap的映射数组中,并返回数组的键值key;然后registerService通过NsdManager的AsyncChannel对象向目标发送REGISTER_SERVICE消息,发送的消息参数包括putListener函数返回的key以及NsdServiceInfo信息。

3、NsdService服务收到REGISTER_SERVICE消息后,首先根据消息源从mClients数组中获得clientInfo对象,然后调用getUniqueId获得一个UniqueId作为登记请求ID;接着调用服务端的registerService函数,registerService的参数为UniqueId和消息传进来的NsdServiceInfo信息。在registerService函数中调用NativeDaemonConnector对象的execute函数,execute函数的命令参数为”mdnssd”,其它参数包括登记命令名称标示"register"、登记ID、从NsdServiceInfo中获得的ServiceName、ServiceType和port等参数。

NativeDaemonConnector对象在NsdService服务实例化时实例化, NativeDaemonConnector对象实例化mSocket参数为"mdns",mCallbacks参数指向NsdService服务内部NativeCallbackReceiver对象。NativeDaemonConnector对象本身是一个派生自Runnable的线程对象,因此其线程函数run也在实例化后启动。

在run函数中首先实例化和启动了一个事件处理线程HandlerThread及其事件处理Handler,接着进入while循环调用listenToSocket函数。

listenToSocket首先实例化一个本地socket对象,LocalSocket对象的LocalSocketAddress地址的 Socket名称为已初始化的mSocket,并使用该地址调用connect函数,从init.rc 可以看到名称为"mdns"的Socket对应的本地服务为netd,因此NativeCallbackReceiver对象与netd服务建立了连接;然后listenToSocket函数调用socket的getInputStream和getOutputStream函数获得输入和输出流对象;最后listenToSocket函数进入while循环不断从输入流读取事件进行分析。解析后的事件发给HandlerThread线程的Handler函数进行处理,在Handler函数中调用mCallbacks的onEvent回调函数,即NsdService服务内部NativeCallbackReceiver对象的onEvent回调函数。

4、在NativeDaemonConnector对象的execute函数中首先根据传进的参数调用makeCommand函数生成一个字符串类型的命令,然后调用本地socket的输出流对象 mOutputStream的write函数来发送命令。

5、在本地服务netd的进程中调用其MDnsSdListener对象的startListener函数启动命令的监听。

MDnsSdListener对象通过FrameworkListener间接派生自SocketListener,在MDnsSdListener对象实例化时其成员mSocketName初始化 为"mdns",因此对应的socket通道和NativeCallbackReceiver对象中的socket通道相同。MDnsSdListener实例化时还初始化一个Monitor对象和一个FrameworkCommand类型的Handler对象。

Handler对象初始化时其mCommand属性赋值为"mdnssd",用来和发送来的命令匹配,Handler对象也保存到FrameworkCommand命令列表对象中mCommands。

Monitor对象实例化时调用socketpair函数建立一个Socket组mCtrlSocketPair,还创建一个监听线程,线程中调用Monitor对象的run函数。

6、startListener函数首先调用android_get_control_socket函数根据mSocketName名称获得其SOCKET fd;然后调用listen函数监听socket通道;

然后创建一个线程,在线程中执行runListener函数,在runListener函数循环调用accept接收客户端连接。当有客户端连接后,根据accept返回的socket fd实例化一个SocketClient对象保存到SocketClient对象列表中mClients,并调用onDataAvailable函数。

onDataAvailable函数调用read函数读取客户端发送的命令,并调用dispatchCommand函数提交命令。

在dispatchCommand函数中解析命令参数,并与mCommands命令对象列表进行命令匹配,并调用匹配后命令对象的runCommand函数,这里即调用MDnsSdListener对象中的Handler对象的runCommand函数。

7、在Handler对象的runCommand函数中进行命令参数的匹配,这里匹配的是"register",因此在获得命令参数后调用serviceRegister函数,serviceRegister函数参数包括匹配的SocketClient对象以及命令参数信息。

8、在serviceRegister函数中,首先调用mMonitor的allocateServiceRef函数根据请求ID实例化一个Element对象放入链表中,并返回Element对象的DNSServiceRef指针,DNSServiceRef指向_DNSServiceRef_t结构,其成员包括DNS操作或应答类型,接收消息回调接口、客户端回调和上下文、客户端与服务端连接socket等参数。

然后调用DNSServiceRegister函数,DNSServiceRegister函数用来向本地MDns后台服务发起连接和消息请求,DNSServiceRegister函数的参数包括allocateServiceRef函数返回的DNSServiceRef指针变量以及serviceRegister传进来的命令请求参数,以及事件接收回调函数MDnsSdListenerRegisterCallback。

在DNSServiceRegister函数调用后接着调用mMonitor的startMonitoring函数,参数为请求ID,startMonitoring函数用来准备与服务器已建立连接的SOCKET监视通道和启动监视通道的监听。最后调用SocketClient对象的sendMsg函数向客户端返回CommandOkay应答消息。

9、DNSServiceRegister函数为mDNSResponder开源工程提供的客户端调用API接口,用来与MDns后台服务建立连接,并向其提交请求。

在DNSServiceRegister函数中首先通过ConnectToServer函数与MDns后台服务建立连接。

在ConnectToServer函数首先实例和初始化一个_DNSServiceRef_t类型DNSServiceOp变量,然后创建一个本地socket,且新建socket的文件句柄赋值给DNSServiceOp对象的sockfd。

然后调用connect与MDns后台服务建立连接,最后把实例化后的DNSServiceOp对象通过DNSServiceRef参数带回。

ConnectToServer函数返回后接着调用create_hdr函数为实例化一个ipc_msg_hdr类型的请求消息,并对请求消息赋值后连同ConnectToServer函数带回的DNSServiceRef参数一同传给deliver_request函数,通过deliver_request函数提交请求。

10 、在Monitor对象的run函数中循环对mPollFds进行poll操作。

在startMonitoring函数通过向mCtrlSocketPair[1]写入RESCAN命令后,由于mPollFds[0].fd指向mCtrlSocketPair[0],因此mMonitor的run函数在mPollFds[0]通道读取到RESCAN命令并调用RESCAN函数,在RESCAN函数中根据已建立的与服务器的连接为mPollFds的其它通道赋值,这些mPollFds通道的文件句柄位赋值为服务器已建立连接的socket 的句柄。

在服务端的响应事件到来时在这些通道poll到事件,然后调用DNSServiceProcessResult函数,参数为DNSServiceRef。

11、 在DNSServiceProcessResult函数中读取响应事件和数据,并调用DNSServiceRef参数的事件回调ProcessReply函数,即对于服务登记请求对应的是MDnsSdListenerRegisterCallback函数。

在MDnsSdListenerRegisterCallback中向Handler对象的监听对象的sendBroadcast函数发送ResponseCode::ServiceRegistrationSucceeded应答消息,Handler对象的监听对象为MDnsSdListener对象本身,因此这里调用SocketListener的sendBroadcast函数。

在sendBroadcast函数中遍历mClients对象的成员对象,并调用其调用sendMsg函数,即调用SocketClient的sendMsg函数。

在sendMsg函数中通过与客户端(即NsdService服务的NativeDaemonConnector对象)建立的SOCKET向客户端发送应答消息。

12、在NsdService的NativeDaemonConnector对象的listenToSocket函数 收到服务端的应答消息后,调用NsdService服务内部NativeCallbackReceiver对象的onEvent回调函数。

在onEvent回调函数中向NsdService服务的状态机发送NsdManager.NATIVE_DAEMON_EVENT事件,假如这时NsdService服务处于EnabledState状态,状态机收到NsdManager.NATIVE_DAEMON_EVENT事件后调用handleNativeEvent函数。

handleNativeEvent函数首先根据响应消息的请求ID从mIdToClientInfoMap中获得先前客户端建立连接时保存的clientInfo对象及从clientInfo对象获得clientId,然后执行响应事件代码为NativeResponseCode.SERVICE_REGISTERED的事件处理,事件处理先根据返回的响应事件实例化一个NsdServiceInfo对象,然后通过clientInfo中的AsyncChannel对象成员向NsdService服务的客户端发送NsdManager.REGISTER_SERVICE_SUCCEEDED响应事件。

13、NsdManager的事件接收对象ServiceHandler接收到NsdManager.REGISTER_SERVICE_SUCCEEDED响应事件,在其handleMessage函数中调用其监听对象(NSD应用客户端)的onServiceRegistered回调。到此整个服务登记流程结束。

服务发现和服务地址解析流程采用服务登记流程基本相同的流程。在服务发现和服务地址解析后就可以向服务收发数据了。

android 发现服务,Android服务之网络服务发现服务相关推荐

  1. 网络舆情监测管理制度及处置机制,网络舆情监测服务项目实时方案?

    舆情监测通常包括搜集.分析和报告舆论信息的过程.搜集信息的途径包括网络新闻.社交媒体.博客.论坛等,分析信息的方法包括自然语言处理.数据挖掘.模糊属性决策分析等.接下来TOOM舆情监测小编带您简单了解 ...

  2. android自带的nsd发现服务器,Android网络服务发现(NSD)协议的使用

    Android的网络服务发现协议(NSD)可以用于在小范围的网络中发现邻近设备上的某个应用.这对于一些社交网络.多人游戏类的应用会非常有帮助. Android的NSD的使用方法大致上分为四种操作: 1 ...

  3. android p2p 连接服务器上,当通过Wi-Fi P2P使用网络服务发现时无法连接到Android设备每个人都可以使用网络服务发现...

    ! 我正在开发一个Android应用程序,允许与附近已安装此应用程序的设备聊天.为了做到这一点,我使用Wi-Fi P2P API和网络服务发现来搜索附近的设备. 我已经编写了用于在服务启动的线程中搜索 ...

  4. 关于“自己电脑无法在网络中发现”问题

    发现问题:本人WIN7系统,之前在设置网络共享之后,打印机可以共享使用,后来在电脑系统更新之后,发现自己电脑无法再网络中发现,因此只能自己使用打印机了,而其他电脑无法使用,针对这个问题,本人尝试了很多 ...

  5. android自带的nsd发现服务器,Android NSD不会发现所有服务

    我试图运行一个应用程序使用Android本地服务发现,但有时当我运行该应用程序,它不会发现我的网络的所有服务.我运行的代码从 https://github.com/joeluchoa/nsd使用四个星 ...

  6. Android学习笔记第五篇--网络连接与云服务(一)

    Android学习笔记第五篇–网络连接与云服务 第一章.无线连接设备 ​ 除了能够在云端通讯,Android的无线API也允许在同一局域网内的设备通讯,**甚至没有连接网络,而是物理具体相近,也可以相 ...

  7. 基于BAS算法实现复杂网络社区发现问题——附python代码

    基于智能优化算法的复杂网络社区发现问题 第一章 基于天牛须算法求解复杂网络社区发现问题 文章目录 基于智能优化算法的复杂网络社区发现问题 前言 一.基本天牛须算法 二.关于社区发现 基本问题 总结 前 ...

  8. android service前台服务器,Android网络前台向服务端页面请求数据

    这是一个android前台向服务端网站请求少量数据的小例子. 首先设计好服务端,再来写安卓前端. 一:服务端 新建动态网站,在java包下新建一个类继承HttpServlet父类,重写doGet()方 ...

  9. android四大组件 服务,Android四大组件之Service

    Service Service(服务)是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件.服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行. 此外,组件可以绑定到 ...

最新文章

  1. Java 常用API的运用,效率及技巧
  2. 分子排列不同会导致_生物信息遇上Deep learning(7): ReLeaSE--强化学习做药物分子设计...
  3. seg显示时间——51程序
  4. html网页制作图案,巧用CSS滤镜做图案文字-网页设计,HTML/CSS
  5. redis aof 备份和恢复_Redis 持久化机制的介绍,了解这些流程很重要
  6. 教室信息管理系统mysql_教师信息管理系统(方式一:数据库为oracle数据库;方式二:存储在文件中)...
  7. 仓库处理中 无法修改_临沂用友U8erp系统软件如何新增仓库?
  8. java 普通类request_[Java教程]spring在普通类中获取session和request
  9. 主题:Hibernate/Spring/Struts架构使用OpenSessionInView的问题
  10. IBM 语音识别输入系统
  11. zabbix--自动注册
  12. ubuntu怎么将Dash切换位bash
  13. 【OpenCV4】计算对称矩阵特征值和特征向量 cv::eigen() 用法详解和代码示例(c++)
  14. 图像的三次B样条插值原理与C++实现
  15. 计算机软件系统验证报告,检验报告管理系统软件
  16. 如何设置csdn为谷歌浏览器默认搜索引擎
  17. iReport表达式
  18. yy安全中心官网首页登录html,YY安全中心
  19. 某女28天断食全记录,120—84
  20. 肖特基和快恢复二极管区别

热门文章

  1. Flutter——最详细(GridView)使用教程
  2. 网络抓包工具 Wireshark 和 tcpdump(三)
  3. jvm内存模型:jvm的理解
  4. Java面向对象之五指棋
  5. 关注朱令--十年前铊中毒的女孩(本文转自http://www8.tianya.cn)
  6. Alder Lake会是英特尔的救世主吗?
  7. tp6整合腾讯云cos上传
  8. 如何识别笔记本样机(2012版)
  9. Redis 应用 一
  10. 表单标签库与数据绑定