在应用到Linux内核之间需要一个桥梁,这个桥梁就是Netd守护进程,我们就从Netd守护进程开始去了解一些Android网络系统的工作流程。
Netd进程是通过init进程启动的,我们来看看它在init.rc中的定义:
service netd /system/bin/netd
class main
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
socket mdns stream 0660 root system
显然netd启动时创建三个TCP监听socket,其名称分别为netd、dnsproxyd和mdns。当设备启动后,到/dev/socket/下会看见有netd、dnsproxyd和mdns这三个文件。netd守护进程的Socket资源和netd、dnsproxyd和mdns名称绑定起来。netd通过Socket接收并处理来自Framework层中NetworkManagementService或NsdService的命令。netd通过Socket接收并解析来自Kernel的UEvent消息,然后再通过Socket转发给Framework层中对应Service去处理。为了证实我们的想法,我们先来看一下TCP/IP网络进程间通信的流程,只有了解Socket通信的流程我们才更容易理解netd的工作流程:
服务器端
客户端
1.创建socket
1.创建socket
2.bind()

3.listen()

4.accecp()

等待客户端连接……
2.connect()
5.读数据(recv)
3.写数据(send)
6.写数据(send)
4.读数据(recv)
7.关闭socket(closesocket())
5.关闭socket(closesocket())
也就是说netd和Framework层,以及netd和kernel之间要通过Socket来通信的话,所做的事情肯定是上面提到的这些。
下面分析netd的main函数
main()
{
//new NetlinkManager对象nm
if (!(nm = NetlinkManager::Instance())) {
ALOGE(“Unable to create NetlinkManager”);
exit(1);
};
//new CommandListener对象cl,将cl设置成nm(NetlinkManager)的消息发送者(mBroadcaster)
cl = new CommandListener(rangeMap);
nm->setBroadcaster((SocketListener *) cl);

/*
*分析CommandListener的构造函数后发现,CommandListener将创建名为“netd”的监听Socket
*/

//启动nm(NetlinkManager)
if (nm->start()) {
ALOGE(“Unable to start NetlinkManager (%s)”, strerror(errno));
exit(1);
}

//创建DnsProxyListener对象dpl,并启动监听,DnsProxyListener将会创建名为“dnsproxyd”监听Socket
dpl = new DnsProxyListener(rangeMap);
if (dpl->startListener()) {
ALOGE(“Unable to start DnsProxyListener (%s)”, strerror(errno));
exit(1);
}

//创建MDnsSdListener对象mdnsl,并启动监听,MDnsSdListener将会创建名为“mdns”的监听Socket
mdnsl = new MDnsSdListener();
if (mdnsl->startListener()) {
ALOGE(“Unable to start MDnsSdListener (%s)”, strerror(errno));
exit(1);
}

//启动cl(CommandListener)监听
if (cl->startListener()) {
ALOGE(“Unable to start CommandListener (%s)”, strerror(errno));
exit(1);
}
}
netd的main函数比较简单,就是创建几个重要的成员并启动成员的工作,笔者这里只讲解netd里重要的成员NetlinkManager。下面看NetlinkManager的类图。

显然NetlinkManager里有三个NetlinkHandler和一个SocketListener对象,NetlinkHandler继承于NetlinkListener,而NetlinkListener又继承于SocketListener。由代码nm->setBroadcaster((SocketListener *) cl);可以知道NetlinkManager里的mBroadcaster其实就是由CommandListener向上转型来的,而CommandListener又继承于FrameworkListener,FrameworkListener继承于SocketListener。
分析NetlinkManager的start函数
int NetlinkManager::start() {
//创建接收NETLINK_KOBJECT_UEVENT消息的socket,其值保存在mUeventSock中
if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
return -1;
}

//创建接收RTMGPR_LINK消息的socket,其值保存在mRouteSock中
if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_IFADDR,
NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
return -1;
}

//创建接收NETLINK_NFLOG消息的socket,其值保存在mQuotaSock中
if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
ALOGE(“Unable to open quota2 logging socket”);
// TODO: return -1 once the emulator gets a new kernel.
}

return 0;
}

NetlinkManager的start函数就是向Kernel注册了三个用于接收UEvent事件的socket。我们接着进到setupSocket里看看,看是不是创建了Socket了,是不是bing了?
NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,
int groups, int format) {

//创建Socket
if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
ALOGE(“Unable to create netlink socket: %s”, strerror(errno));
return NULL;
}

if (bind(sock, (struct sockaddr ) &nladdr, sizeof(nladdr)) < 0) {
ALOGE(“Unable to bind netlink socket: %s”, strerror(errno));
close(*sock);
return NULL;
}

//如果没有猜错在NetlinkHandler的start里一定会调用listen接着另起一线程进行accept和等待客户端的连接以及不停的接收来自kernel的UEvent消息。
NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
if (handler->start()) {
ALOGE(“Unable to start NetlinkHandler: %s”, strerror(errno));
close(*sock);
return NULL;
}

}

int SocketListener::startListener() {

//调用listen
if (mListen && listen(mSock, 4) < 0) {
SLOGE(“Unable to listen on socket (%s)”, strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));

//另起线程
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE(“pthread_create (%s)”, strerror(errno));
return -1;
}

}

void SocketListener::runListener() {

while(1) {

   //调用selectif ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {if (errno == EINTR)continue;SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);sleep(1);continue;} else if (!rc)continue;

}

   if (mListen && FD_ISSET(mSock, &read_fds)) {//调用acceptdo {alen = sizeof(addr);c = accept(mSock, &addr, &alen);SLOGV("%s got %d from accept", mSocketName, c);} while (c < 0 && errno == EINTR);

}

}

当我们拔掉终端的网线时,我们监听的Socket会收到RTMGPR_LINK事件。来到这里我们已经知道NetlinkManager是怎么收到来自kernel的UEvent事件的,但是收到这个UEvent事件后,netd又是怎么通知Framework层的呢?
NetlinkHandler接收到的UEvent消息会转换成一个NetlinkEvent对象。NetlinkEvent对象封装了对UEvent消息的解析方法,UEvent消息经解析后将经由mBroadcaster对象传递给Framework层的接收者。我们下面将分析mBroadcaster来验证我们的想法。
上面已经说过了mBroadcaster其实就是CommandListener向上转型得到的东西,我们可以从CommandListener着手分析。
CommandListener::CommandListener(UidMarkMap *map) :
FrameworkListener(“netd”, true) {
//注册11个命令类对象
registerCmd(new InterfaceCmd());
registerCmd(new IpFwdCmd());
registerCmd(new TetherCmd());
registerCmd(new NatCmd());
registerCmd(new ListTtysCmd());
registerCmd(new PppdCmd());
registerCmd(new SoftapCmd());
registerCmd(new BandwidthControlCmd());
registerCmd(new IdletimerControlCmd());
registerCmd(new ResolverCmd());
registerCmd(new FirewallCmd());
registerCmd(new ClatdCmd());

}
CommandListener的构造函数里最主要的函数是FrameworkListener(“netd”, true),显然验证了上面说法,这里要创建名为“netd”的监听Socket。在netd的main函数里我们有调用过cl->startListener(),我们到CommandListener的startListener里看看,CommandListener的startListener调用其实就是其父类的父类SocketListener的startListener。
int SocketListener::startListener() {
if (!mSocketName && mSock == -1) {
SLOGE(“Failed to start unbound listener”);
errno = EINVAL;
return -1;
} else if (mSocketName) {
//获取名称为“netd”的监听Socket
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
SLOGE(“Obtaining file descriptor socket ‘%s’ failed: %s”,
mSocketName, strerror(errno));
return -1;
}
SLOGV(“got mSock = %d for %s”, mSock, mSocketName);
}

//调用listen
if (mListen && listen(mSock, 4) < 0) {
SLOGE(“Unable to listen on socket (%s)”, strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));

//创建新线程
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE(“pthread_create (%s)”, strerror(errno));
return -1;
}

}

分析到这里下面就不需要详细讲了,因为和刚才创建Socket监听kernel的UEvent事件的过程一样。到这里netd的工作基本已经清楚了,就是获取名称为“netd”的Socket并启动监听,同时创建3个Socket用于监听来自kernel的3中不同的UEvent事件,解析kernel发送过来的UEvent事件,将解析结果通过名称为“netd”的监听Socket发送给Framework层的服务,同时名称为“netd”的Socket也接收来自Framework层的消息(即用户通过Framework层设置网络的命令和信息),把接收到的消息在通过相应的Socket发送给kernel。整个过程中netd进程一直是Socket通信的服务端,而kernel的客户端的代码是怎么样子的,笔者这里不讲,因为那是驱动的事情,而作为另外一个客户端Framework层里的代码到底是什么样子的呢?

public static NetworkManagementService create(Context context) throws InterruptedException {
// NETD_SOCKET_NAME就是我们要找的“netd”
return create(context, NETD_SOCKET_NAME);
}

static NetworkManagementService create(Context context,
String socket) throws InterruptedException {
final NetworkManagementService service = new NetworkManagementService(context, socket);

//启动线程
service.mThread.start();

}

到NetworkManagementService的构造函数看看
private NetworkManagementService(Context context, String socket) {

mConnector = new NativeDaemonConnector(
new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
mThread = new Thread(mConnector, NETD_TAG);

}

我们到NativeDaemonConnector的run函数里去看看,是不是在run函数里,都做了客户端需要做的事情,以接收和发送消息给netd?

public void run() {

listenToSocket() {
//创建Socket
socket = new LocalSocket();

//设置连接地址
LocalSocketAddress address = determineSocketAddress();

//建立连接
socket.connect(address);

//获取输入流
InputStream inputStream = socket.getInputStream();

//获取输出流
mOutputStream = socket.getOutputStream();
}

}

分析到这里,我们已经知道在Framework层和netd通信的服务是NetworkManagementService。读完这篇文章后,大伙应该基本了解Android系统的以太网的工作流程了,关键是我们学到了Android的其中一个设计方法,Framework层怎么和Native层的另外一个进程通信,我们可以模仿上面讲到的方法,用Socket。

android netd和kernelframeworks的通信逻辑相关推荐

  1. android系统(106)---Android Netd ndc

    Android Netd ndc (Native Daemon Connector) http://blog.chinaunix.net/uid-23381466-id-5112474.html Ne ...

  2. 手机APP开发之MIT Appinventor详细实战教程(一),利用通过蓝牙控制单片机,以及实现单片机与android设备之间的串口通信

    目录 (一)前期软件准备和硬件准备 ( 二 ) 实现的思路和操作原理 ( 三) 具体的操作方法 MIT Appinventor 是编程领域较为受欢迎且适用的编程软件 ,因其操作流程和使用方法简单,一直 ...

  3. 如何在Android平台下进行Socket通信

    如何在Android平台下进行Socket通信 首先在Java se平台上新建一个Socket服务端: public static void main(String[] args) { try { S ...

  4. android fragmentactivity fragment,Android:Activity与Fragment通信(99%)完美解决方案

    前言 最近一直在想着能否有一种更好的方案来解决:Android中Activity与Fragment之间通信的问题,什么叫更好呢,就是能让Fragment的复用性高,性能还有好(不用反射),代码还要好维 ...

  5. (转载) Android两个子线程之间通信

    Android两个子线程之间通信 标签: classthreadandroid子线程通信 2015-03-20 17:03 3239人阅读 评论(0) 收藏 举报  分类: 个人杂谈 版权声明:本文为 ...

  6. android 服务端 servlet webservice,Tomcat 上如何部署Servlet及Android中如何与服务器通信(12页)-原创力文档...

    Tomcat 上如何部署Servlet及Android中如何与服务器通信 下载Tomcat并安装 Apache Tomcat powers numerous large-scale, mission- ...

  7. Android学习笔记---22_访问通信录中的联系人和添加联系人,使用事物添加联系人...

    Android学习笔记---22_访问通信录中的联系人和添加联系

  8. 手把手教你Android手机与BLE终端通信--连接,发送和接收数据

    假设你还没有看上一篇 手把手教你Android手机与BLE终端通信--搜索,你就先看看吧,由于这一篇要接着讲搜索到蓝牙后的连接.和连接后的发送和接收数据. 评论里有非常多人问假设一条信息特别长,怎么不 ...

  9. Android通过USB与PC通信

    最近项目中有一个功能需要用到Android与PC端同步数据.查阅了相关资料后,采取了一种建立在adb基础之上的Usb通信方式:由于adb可以将Usb模拟为网卡,所以可以利用socket通信的方式实现A ...

最新文章

  1. 关于js中window.location.href、location.href 等如何跳转
  2. moss中修改master页需要注意的地方
  3. 批处理之字符串处理和数值计算
  4. 如何成为公司独当一面的工程师
  5. 测试技术培训:如何测试磁盘写的速度
  6. 如何修复提交错误的Git分支?
  7. Java基础SQL优化---面试题【一】
  8. Welcome-to-Swift-13继承(Inheritance)
  9. AlphaGo Zero,造神还是开启潘多拉魔盒?【附论文下载】
  10. exce中让两列数据一一对应_EXCEL让两个表格中的两列数据一一对应:
  11. java web象棋教程_【Java学习笔记】实战——网络象棋
  12. 如何制作多链接二维码?一次性防伪二维码?
  13. 智能分数计算机在线使用,作业帮智能计算器在线使用
  14. 好书分享、能量传递-《软技能 代码之外的生存指南》自我营销篇
  15. 【网上商城优惠活动】
  16. 服务改进还是先从自己改起吧
  17. 写在2020年的尾巴
  18. 达内2016前端开发知识点总结--HTML5--8天
  19. C语言实现shell
  20. Oracle清理回收站的方法

热门文章

  1. C程序设计(谭浩强)的几处错误
  2. MATLAB 用高斯消元法求解线性方程组
  3. Java读取hdfs文件权限问题
  4. ASP微信支付之扫码支付
  5. 移动端html尺寸,移动端页面的三种尺寸
  6. 镭速raysync介绍文件传输软件的进史
  7. 行车记录仪摄像头4线
  8. 自媒体运营、平面设计封面如何搭配?3大色彩搭配网站推荐
  9. mysql gtidpurged_7. MySQL复制全解析 Part 7 gtid_next和gtid_purged 系统变量解析
  10. 基于ITK的读并写 2D的DICOM 图像