虽然标题写的是《Android网络通讯原理解析》,实际上,发不管是安卓还是linux系统或者是塞班,Windows,Mac OS还是其他各种各样的操作系统,其内部的网络通讯原理基本一致;有些小伙伴想知道安卓两个应用之间胡这是从安卓应用到服务主机之间数据传输是怎么实现的,但是搜到的大部分讲的比较抽象,因此我决定写一篇文章把这这些知识点串联起来,让大家有一个更加全面的认识;
我们先来看一个场景吧;
我写了一个Http请求图片的应用,然后我把这个应用安装在了我的小米手机上。我打开应用,程序从后台服务器请求到了图片并加载在ImageView上;
这个场景我相信大家再熟悉不过了,我通过时序图把这个画出来;
从使用者的角度大概就是上图这样了。由于我们在开发过程中可以使用HttpClient或者其他三方网络请求组件进行网络请求,通常开发人员不会关心HttpClient请求之后经历了那些过程才拿到数据,只需要关心请求是否成功。
细心一点的小伙伴肯定知道不管是volly还是HttpImageLoader还是安卓原生组件httpUrlConnection,都是用的Http进行网络请求;那么Http的下一层又是如何进行网络请求的呢?下下一层呢?下下下一层呢?最终这些数据是如何到服务器主机中经过处理又回来的呢?
相信大家对下面这张图都不陌生,这是OSI七层模型和简化过的Tcp模型;
我们可以直接看右边这个模型,对于开发人员来说,能够接触到的最直观能够感受到的就是应用层;如果我们把上面HttpClient请求网络的例子画在这个模型里面会是这样的;
细心的朋友会发现,Socket不是应用层也不是传输层?就像下面这张图一样:
为了搞清楚这个问题,我们需要了解一下Socket的定义;
Socket:套接字,TCP/IP网络的API。(港口码头/车站)Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
这就像门槛,他既不属于门内又不属于门外。他是连接屋内和屋外的一个抽象层的工具;而且通过这个图我们知道,HttpClient其实是用Http实现的,Http就是我们说的短连接,虽然我们在使用Http Api的时候发现他有提供Keep-Alive属性设置,但是实际上这个Keep-Alive也是很短的;准确来说,Http的设计就是为了实现短连接,实现短连接有很多好处,它可以节约网络资源(服务器能够承载的连接数量是有限的),它还能够节省流量,和一些其它很实用的功能。
但是我们在使用Socket发送数据的时候我们发现要么是Tcp要么是UDP,我们无法通过它发送这两个协议意外的数据。那是不是网络上只存在这两种协议的数据呢?而Socket发送数据的时候调用的方法叫做sendPacket,也就是我们发送的数据或进行封装成一个数据包再发出去;那Packet是什么Packet呢?其实就是封装了TCP/UDP的Packet;
我们使用Socket发送数据的时候,只需要将我们想要发的数据byte[]丢到sendPacket的参数中,Socket Api会按照Socket的配置对这个数据进行封装;这个封装其实也很简单,就是给这段数据加了一些协议头,为了更加直观的看出来这个封装过程,大家可以看下这个图:
关于TCP/UDP封装的具体细节这里就不多说了,有兴趣的朋友可以去查阅相关资料。但是大体上就是这样,Socket先给它加了一个TCP协议头,然后给他加了个IP协议头,这个时候就构成了一个IP数据报;那Socket封装好数据报之后把这个数据报交给谁呢?直接交给网卡?当然不是,在一般情况下我们想要操作硬件,是需要通过操作系统内核进行操作,就拿Linux来说,网卡在linux上就是个设备文件,应用通过共享内存或者其他的方式操作这个文件,硬件驱动就会读取这个文件从而进行相应的操作;这里面知识点比较多,但是数据在这个过程中还是没变的,所以我就不多说,大概知道是这么回事就行了;
上面一直在软件层走,现在终于跟硬件沾上边了;不过懂点硬件的人都知道,要想使用硬件,首先要有一个驱动程序,通过这个驱动程序提供的接口来使用该硬件;我们在安装安装网卡的时候知道有个网卡驱动需要安装,对,就是这个玩意;
大家配合上面那张图看,linux网卡驱动程序,将IP包添加14字节的MAC包头,构成MAC包。MAC包中含有发送端和接收端的MAC地址信息。既然是驱动程序创建的MAC包头信息,当然可以随便输入地址信息的。主机伪装就是这么实现的。
驱动程序将MAC包拷贝到网卡芯片内部的缓冲区,就算完事了。有网卡芯片接手处理。
网卡芯片对MAC包,再次封装成物理帧,添加头部同步信息和CRC校验。然后丢到网线上,就完成一个IP报文的发送。所有挂接到本网线的网卡都可以看到该物理帧。
发送讲完了,讲接受就简单多了。接受就是将发送的过程反过来;数据从网线先到物理层,再到以太网驱动,再到Socket,再到应用层;我参考了一下网上一个汽车和高速公路的例子,觉得很松动;
网线可以看作一个高速公路,物理帧也就是辆汽车,网卡呢,或许是个加油站吧。 从这个角度讲,汽车和加油站没有绝对的关系,所有汽车都可以进入该加油站。
网线上的物理帧首先被网卡芯片获取,网卡芯片会检查物理帧的CRC(对应上图中以4个字节太网尾部),保证完整性。其次,网卡芯片将物理帧头去掉,得到MAC包。
网卡芯片检查MAC包内的目的MAC地址信息,和本网卡的MAC地址是否一致?不一致,抛弃。
网卡芯片将MAC帧拷贝到网卡的内部缓冲区,触发中断。
驱动程序通过中断,将MAC包拷贝到系统中,构建sk_buff。告诉上层。
上层去掉MAC包头,得到需要的IP包。
在上面过程中,网卡芯片对物理帧进行了MAC匹配过滤。这样做可以减小系统负荷。试想一下,若网卡芯片对所有的MAC帧不加判断的直接提供给驱动,让CPU判决会是什么样子呢?当总线上数据繁忙,CPU将浪费大部分时间去判断该MAC包是否是自己需要的,效率低下。
那有没有可能监听到不是属于自己的包呢?答案是可以的;实际上大部分网卡都支持设置混听模式,也就是说你可以拿你的电脑监听网络上其他人的数据,而且只要修改网卡监听模式就可以了(好怕怕);
网线上的物理帧首先被网卡芯片获取,网卡芯片会检查物理帧的CRC,保证完整性。其次,网卡芯片将物理帧头去掉,得到MAC包。网卡芯片发现自己当前被配置为混听模式,就不对MAC包过滤。网卡芯片将MAC帧拷贝到网卡内部的缓冲区,触发中断。驱动程序通过中断,将MAC包拷贝到系统中,构建sk_buff,告诉上层。上层去掉MAC包头,得到需要的IP包。显然,这里的IP包并不一定是发给自己的。
实际上,除了使用混听模式外,我们也可以通过修改驱动来监听不属于自己的包;其实这个很好理解,因为过滤包是通过网卡过滤的,如果我们让网卡的过滤机制失效,那么我们就能收到任何网内的包;
网卡到底能不能接收其他MAC包,完全取决于网卡芯片中RCR( receive control register )配置。驱动程序是决定网卡能否工作于混听模式的桥梁。混听模式会加重CPU的负荷,而且也是不符合标准应用的!
大家想一下,所有的车辆都要从加油站穿过,(有些都不加油),加油站工作人员的任务量就可想而知。
除了以上两种方式,还有另一种不修改驱动就能让上层应用监听非自己的包的方式,那就是直接访问网卡芯片RCR达到设置混听模式。