起名TCP的一遍握手(建立连接)和柒回挥手(关闭连接)

TCP(Transmission Control Protocol) 传输控制协议 一遍握手
TCP是主机对主机层的传输控制协议,提供保险的连接服务,接纳三遍握手确认建立二个连接:
位码即tcp标志位,有6种标志: SYN(synchronous建立联合) ACK(acknowledgement
确认) PSH(push传送) FIN(finish截止) 奥迪Q3ST(reset重置) ULANDG(urgent火急)
Sequence number(顺序号码) Acknowledge number(确认号码)
客户端TCP状态迁移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器TCP状态迁移:
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
种种状态的意义如下: LISTEN – 侦听来自国外TCP端口的连续请求; SYN-SENT
-在出殡和埋葬连接请求后等候匹配的连天请求; SYN-RECEIVED –
在吸收和发送叁个两次三番请求后等候对连日请求的承认; ESTABLISHED-
代表一个打开的总是,数据足以传递给用户; FIN-WAIT-1 –
等待远程TCP的接连中断请求,或先前的连接中断请求的承认; FIN-WAIT-2 –
从远程TCP等待连接中断请求; CLOSE-WAIT –
等待从本地用户发来的三番五次中断请求; CLOSING
-等待远程TCP对连年中断的认可; LAST-ACK –
等待原来发向远程TCP的连日中断请求的承认; TIME-WAIT
-等待丰富的时刻以保障远程TCP接收到连年中断请求的承认; CLOSED –
没有其他连接情状;
TCP/IP协议中,TCP协议提供保障的接连服务,采取1次握手建立二个接连,如图1所示。
(1)第叁次握手:建立连接时,客户端A发送SYN包(SYN=j)到劳动器B,并跻身SYN_SEND状态,等待服务器B确认。
(2)第二回握手:服务器B收到SYN包,必须认同客户A的SYN(ACK=j+1),同时自个儿也发送三个SYN包(SYN=k),即SYN+ACK包,此时劳动器B进入SYN_RECV状态。
(3)首回握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送达成,客户端A和劳动器B进入ESTABLISHED状态,完结一回握手。
实现1回握手,客户端与服务器发轫传送数据。 第三遍握手:
客户端发送一个TCP的SYN标志地方1的包指明客户打算连接的服务器的端口,以及初阶序号X,保存在桂林的类别号(Sequence
Number)字段里。 首回握手:
服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1同时,将肯定序号(Acknowledgement
Number)设置为客户的I S N加1以.即X+1。 第1遍握手.
客户端再一次发送确认包(ACK)
SYN标志位为0,ACK标志位为1.同时把服务器发来ACK的序号字段+1,放在规定字段中发送给对方.并且在数据段放写ISN的+1
关闭连接:
由于TCP连接是全双工的,由此每一种方向都必须独立实行倒闭。这么些条件是当一方完结它的多少发送职分后就能发送三个FIN来终止这么些主旋律的总是。收到三个FIN只表示这一趋势上尚未数量流动,二个TCP连接在吸收多少个FIN后还能发送数据。首先实行倒闭的一方将执行积极关闭,而另一方执行被动关闭。
CP的连天的拆卸须要发送多少个包,由此称为六次挥手(four-way
handshake)。客户端或服务器均可积极发起挥手动作,在socket编制程序中,任何一方执行close()操作即可发生挥手操作。
(1)客户端A发送2个FIN,用来关闭客户A到劳动器B的数码传送。
(2)服务器B收到那么些FIN,它发回2个ACK,确认序号为收到的序号加1。和SYN一样,贰个FIN将占用贰个序号。
(3)服务器B关闭与客户端A的总是,发送三个FIN给客户端A。
(4)客户端A发回ACK报文确认,并将肯定序号设置为接到序号加1。
TCP选择伍回挥手关闭连接如图2所示。 图2 TCP六次挥手关闭连接
参见wireshark抓包,实地衡量的抓包结果并从未严刻按挥手时序。小编猜测是岁月间隔太短造成。
深远通晓TCP连接的释放:
由于TCP连接是全双工的,因而各个方向都无法不独立开始展览关闭。这标准是当一方达成它的数目发送任务后就能发送三个FIN来终止这些主旋律的连天。收到二个FIN只象征这一样子上并未多少流动,七个TCP连接在收到二个FIN后还能发送数据。首先进行关闭的一方将执行积极关闭,而另一方执行被动关闭。
TCP协和式飞机的连接是全双工连接,1个TCP连接存在双向的读写通道。 不难说来是
“先关读,后关写”,一共须要四个级次。以客户机发起关闭连接为例:
1.服务器读通道关闭 2.客户机写通道关闭 3.客户机读通道关闭
4.服务器写通道关闭
关闭行为是在发起方数据发送实现之后,给对方发生一个FIN(finish)数据段。直到接收到对方发送的FIN,且对方接到了收取确认ACK之后,双方的数额通讯完全告竣,进度中年老年是接到都亟待回到确认数据段ACK。
详细进程: 第贰品级
客户机发送完数据之后,向服务器发送八个FIN数据段,连串号为i;
1.服务器收到FIN(i)后,重临确认段ACK,类别号为i+1,关闭服务器读通道;
2.客户机械收割到ACK(i+1)后,关闭客户机写通道;
(此时,客户机仍是可以经过读通道读取服务器的数额,服务器还是可以透过写通道写多少)
第③阶段 服务器发送完数据之后,向客户机发送二个FIN数据段,类别号为j;
3.客户机械收割到FIN(j)后,再次来到确认段ACK,种类号为j+1,关闭客户机读通道;
4.服务器收到ACK(j+1)后,关闭服务器写通道。
那是专业的TCP关闭四个级次,服务器和客户机都能够倡导关闭,完全对称。
FIN标识是经过发送最终一块数据时设置的,标准的事例中,服务器还在发送数据,所以要等到发送完的时候,设置FIN(此时可称为TCP连接处于半关闭状态,因为数量仍可从被动关闭一方向积极关闭方传送)。假诺在服务器收到FIN(i)时,已经远非数据要求发送,能够在重返ACK(i+1)的时候就安装FIN(j)标识,那样就一定于能够统一第三步和第叁步。读《Linux互连网编制程序》关闭TCP连接章节,作以下笔记:
TCP的TIME_WAIT和Close_Wait状态
面试时看到应聘者简历中写驾驭互联网,TCP编程,作者常问一个难题,TCP建立连接须要四回握手?95%以上的应聘者都能答对是2回。问TCP断开连接要求两次握手,7/10的应聘者能答对是4遍通信。再问CLOSE_WAIT,TIME_WAIT是何等状态,怎么发生的,对服务有何样影响,如何清除?有部分同室就回应不上来。不是自身扣细节,而是在报导为主的前端服务器上,必须有力量处理种种TCP状态。比如总结在本厂的一台前端机上高峰时刻TCP连接的境况,总括命令:
netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
结果: 除了ESTABLISHED,能够见见连接数相比多的多少个境况是:FIN_WAIT1,
TIME_WAIT, CLOSE_WAIT,
SYN_RECV和LAST_ACK;上面包车型大巴篇章就那个情景的发出条件、对系统的震慑以及处理情势进行简要描述。
TCP状态 TCP状态如下图所示: 大概有点凌乱?再看看这一个时序图
上边看下大家一般比较关切的两种TCP状态 SYN_RECV
服务端收到建立连接的SYN没有收取ACK包的时候处在SYN_RECV状态。有多少个相关系统布置:
1,net.ipv4.tcp_synack_retries :INTEGEHaval 暗中同意值是5
对此远端的三番五次请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个SYN连接请求包。那是所谓的一遍握手( threeway
handshake)机制的第三个步骤。那里决定内核在放任连接之前所送出的 SYN+ACK
数目。不该超过255,暗中认可值是5,对应于180秒左右时间。平时大家不对这几个值举办修改,因为我们期望TCP连接不要因为偶然的丢包而一筹莫展建立。
2,net.ipv4.tcp_syncookies
一般服务器都会安装net.ipv4.tcp_syncookies=1来防备SYN
Flood攻击。就算1个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在爆发SYN+ACK应答报文后是心有余而力不足吸收客户端的ACK报文的(第三次握手不可能做到),那种情况下服务器端一般会重试(再度发送SYN+ACK给客户端)并听候一段时间后甩掉这些未形成的连年,那段时间的长短大家誉为SYN
Timeout,一般的话那些时刻是分钟的数量级(大约为30秒-2分钟)。
那个地处SYNC_RECV的TCP连接称为半老是,并储存在基础的半连接队列中,在基础收到对端发送的ack包时会查找半接连队列,并将符合的requst_sock音讯囤积到成功三遍握手的连年的体系中,然后删除此半老是。大批量SYNC_RECV的TCP连接会导致半连接队列溢出,那样继续的三番五次建立请求会被基本直接丢掉,那正是SYN
Flood攻击。 能够有效防护SYN Flood攻击的伎俩之一,正是SYN Cookie。SYN
Cookie原理由D. J. Bernstain和 埃里克 Schenk发明。SYN
库克ie是对TCP服务器端的贰次握手球组织议作一些改动,专门用来防患SYN
Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并重临TCP
SYN+ACK包时,不分红三个专程的数据区,而是依照那一个SYN包总括出二个cookie值。在吸收TCP
ACK包时,TCP服务器在根据那么些cookie值检查那么些TCP
ACK包的合法性。若是官方,再分配专门的数据区实行拍卖未来的TCP连接。
观测服务上SYN_RECV连接个数为:7314,对于3个高并发连接的广播发表服务器,那些数字比较健康。
CLOSE_WAIT
发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未产生ACK的TCP状态是CLOSE_WAIT。出现那种气象一般都以由于server端代码的难题,假如您的服务器上出现大批量CLOSE_WAIT,应该要考虑检讨代码。
TIME_WAIT 依照TCP协议定义的贰遍握手断开连接规定,发起socket主动关闭的一方
socket将进入TIME_WAIT状态。TIME_WAIT状态将不止三个MSL(马克斯 Segment
Lifetime),在Windows下暗中认可为4秒钟,即240秒。TIME_WAIT状态下的socket不能够被回收利用.
具体情状是对此三个甩卖多量短连接的服务器,借使是由服务器主动关闭客户端的连接,将招致服务器端存在大气的处于TIME_WAIT状态的socket,
甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,结束服务。
为啥要求TIME_WAIT?TIME_WAIT是TCP协议用以保险被重新分配的socket不会遇到在此以前残留的推迟重发报文影响的建制,是必不可少的逻辑保证。
和TIME_WAIT状态有关的种类参数有一般由三个,本厂设置如下:
net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_fin_timeout,默认60s,减小fin_timeout,减少TIME_WAIT连接数量。
net.ipv4.tcp_tw_reuse = 1表示开启重用。允许将TIME-WAIT
sockets重新用于新的TCP连接,暗许为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT
sockets的迅猛回收,默许为0,表示关闭。
为了便于描述,小编给那一个TCP连接的一端起名为Client,给此外一端起名为Server。上航海用教室描述的是Client主动关闭的进程,FTP协议中就那样的。借使要讲述Server主动关闭的经过,只要调换描述进度中的Server和Client就足以了,HTTP协议正是那样的。
描述进度:
Client调用close()函数,给Server发送FIN,请求关闭连接;Server收到FIN之后给Client重回确认ACK,同时关闭读通道(不精晓就去看一下shutdown和close的差异),也正是说未来不可能再从这些一而再上读取东西,现在read重临0。此时Server的TCP状态转化为CLOSE_WAIT状态。
Client收到对友好的FIN确认后,关闭 写通道,不再向连接中写入别的数据。
接下来Server调用close()来关闭连接,给Client发送FIN,Client收到后给Server回复ACK确认,同时Client关闭读通道,进入TIME_WAIT状态。
Server接收到Client对友好的FIN的承认ACK,关闭写通道,TCP连接转化为CLOSED,也正是关闭连接。
Client在TIME_WAIT状态下要等待最大数据段生存期的两倍,然后才进入CLOSED状态,TCP协议关闭连接进度彻底终结。
以上就是TCP协议关闭连接的长河,将来说一下TIME_WAIT状态。
从地方能够看来,主动发起关闭连接的操作的一方将达到TIME_WAIT状态,而且那些情形要保持马克西姆um
Segment Lifetime的两倍时间。为何要那样做而不是一直进去CLOSED状态?
原因有二: 壹 、保险TCP协议的全双工连接能够可相信关闭
贰 、保险此次再而三的重新数据段从网络中消失
先说第2点,假使Client直接CLOSED了,那么由于IP协议的离谱性或然是任何互连网原因,导致Server没有接到Client最终回复的ACK。那么Server就会在逾期今后一连发送FIN,此时出于Client已经CLOSED了,就找不到与重发的FIN对应的再三再四,最后Server就会接受QX56ST而不是ACK,Server就会以为是一连错误把难点报告给高层。那样的景色固然不会导致数据丢失,不过却招致TCP协议不切合保障接二连三的须求。所以,Client不是直接进入CLOSED,而是要保证TIME_WAIT,当再一次接受FIN的时候,能够确认保证对方接到ACK,最终正确的关门连接。
再说第2点,假使Client直接CLOSED,然后又再向Server发起一个新连接,我们不可能保障那一个新连接与刚关门的连日的端口号是例外的。也正是说有可能新连接和老连接的端口号是同等的。一般的话不会发出什么样难题,可是照旧有优异意况出现:要是新连接和早已关门的老连接端口号是千篇一律的,借使前2遍延续的一些数据依然停留在互联网中,这么些延迟数据在确立新连接之后才抵达Server,由于新连接和老连接的端口号是相同的,又因为TCP协议判断差异连接的依据是socket
pair,于是,TCP协议就觉得相当延迟的数目是属于新连接的,这样就和实在的新连接的数码包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,那样能够确保本次连接的全体数据都从互联网中没有。
各个协商都在此之前人句斟字酌后获取的正统,规范。从细节中都能感受到精细和严格。每一遍深切都有同2个感到,精妙。

http://blog.csdn.net/silenceburn/article/details/6083375

===========================================

*
*

*
*

如有错漏请不吝拍砖指正,转发请申明出处,相当感激

 


 

有二个标题,在网上被频仍的问到,正是怎么自定义的Receiver总是不能够收到到SDXC存款和储蓄卡插拔的事件。

而此题材大多数情状下得以因此扩大一句代码解决:
filter.addDataScheme(“file”);  // filter是IntentFilter对象

 

这就是说为啥扩大这句代码就可以消除了吗?那些标题尽管有人问到,可是却尚未太好的应对。

起名,莫不是因为对此领悟IntentFilter策略的高手们来说,那根本算不上难点,是一个再分明然而的谜底而已。

而对于不太掌握IntentFilter策略的我们初学者的话,这几个难题又暂且有点太碍事领会呢。

 

从而,本文试着通过对android的事件过滤策略举行介绍和分析,结合示例程序进行表达,

来解答此难点,并开端的介绍android事件过滤策略。

 


1. 编纂示例程序,创设三个自定义的布罗兹castReceiver

率先大家成立3个android工程起名为SdCardTester,作为示范程序。

为了方便在继续手续中模拟SDXC卡插拔,建议将对象平台设定为2.3版本,使用2.3本子的模拟器。

别的务必注意,运营此示例程序的AVD模拟器供给追加SD存款和储蓄卡成效支撑。

 

接下来为SdCardTester类增添四个BroadcastReceiver类型的积极分子变量 mReceiver。

在onCreate中,使用匿名类的技能,为 mReceiver
赋值3个布罗兹castReceiver子类实例。

[java] view
plain
copy

  1.      mReceiver = new BroadcastReceiver() {  
  2. @Override  
  3. public void onReceive(Context context, Intent intent) {  
  4.         Log.i(“myLoger”,” Receive SDCard Mount/UnMount!”);  
  5. }  
  6. ;  

注意代码中重写的onRecevie函数里只有一句代码,用于记录日志。以验证大家实在收到了风云。

 

接下来创立多个IntentFilter,用于过滤SDXC存款和储蓄卡插拔事件。

最终把大家自定义的Receiver和编写制定好的IntentFilter注册到系统中

[java] view
plain
copy

  1. IntentFilter filter = new IntentFilter();  
  2. filter.addAction(Intent.ACTION_MEDIA_MOUNTED);  
  3. filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);  
  4. registerReceiver(mReceiver, filter);  

 

说到底的尾声不要忘了在onDestory中废除大家的自定义Receiver,

迄今实现了示范程序的代码编写,SdCardTester的完好代码如下:

[java] view
plain
copy

  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.BroadcastReceiver;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.IntentFilter;  
  8. import android.os.Bundle;  
  9. import android.util.Log;  
  10.   
  11. public class SdCardTester extends Activity {  
  12.       
  13.     BroadcastReceiver mReceiver;  
  14.       
  15.     /** Called when the activity is first created. */  
  16.     @Override  
  17.     public void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.main);  
  20.           
  21.         mReceiver = new BroadcastReceiver() {  
  22.             @Override  
  23.             public void onReceive(Context context, Intent intent) {  
  24.                     Log.i(“myLoger”,” Receive SDCard Mount/UnMount!”);  
  25.             }  
  26.         };  
  27.           
  28.         IntentFilter filter = new IntentFilter();  
  29.         filter.addAction(Intent.ACTION_MEDIA_MOUNTED);  
  30.         filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);  
  31.         registerReceiver(mReceiver, filter);  
  32.          
  33.     }  
  34.       
  35.     @Override  
  36.     protected void onDestroy() {  
  37.         // TODO Auto-generated method stub  
  38.         super.onDestroy();  
  39.         unregisterReceiver(mReceiver);  
  40.     }  
  41. }  

 


2. 测试示例程序

运营SdCardTester,等看到hello World
字样,表达onCreate实现,也就表示大家自定义的Receiver也一度运转了。

 

而后,不要用BACK退出,按HOME按钮重返主显示器,(也正是要确认保证大家的自定义Receiver照旧在运转)

进去手提式无线电话机设定,选拔Storage (要是系统语言选用为华语了是“存款和储蓄”),

选料 Unmount SD card / mount SD card
选项,就足以生搬硬套SD内存卡插拔的动作了。如下图所示:

起名 1

 

假设大家的自定义Receiver收到了有关事件,遵照代码实现,就会利用 TAG
“myLoger” 输出日志到logCat。

所以大家通过观望logCat输出鲜明是还是不是有连锁事件爆发,在cmd窗口中运维 ” adb
logcat -s myLoger:i ” ,即可持续监察和控制日志输出。

恐怕应用Eclipse ADT插件扩大的DDMS视图,在logCat
的View中扩展三个filter,如下图所示:

起名 2

 

瞩目,遵照大家眼下的代码落成,无论大家怎么着插拔SD读取卡,都不会看到日志窗口监察和控制到事件发生。

 

OK,我们今后追加那行神奇的代码:“   filter.addDataScheme(“file”);   ”,

并在onCreate中追加三个日志输出监察和控制程序进入。修改之后的代码如下:

[java] view
plain
copy

  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.BroadcastReceiver;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.IntentFilter;  
  8. import android.os.Bundle;  
  9. import android.util.Log;  
  10.   
  11. public class SdCardTester extends Activity {  
  12.       
  13.     BroadcastReceiver mReceiver;  
  14.       
  15.     /** Called when the activity is first created. */  
  16.     @Override  
  17.     public void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.main);  
  20.         Log.i(“myLoger”,” onCreate ……”);  
  21.           
  22.         mReceiver = new BroadcastReceiver() {  
  23.             @Override  
  24.             public void onReceive(Context context, Intent intent) {  
  25.                     Log.i(“myLoger”,” Receive SDCard Mount/UnMount!”);  
  26.             }  
  27.         };  
  28.           
  29.         IntentFilter filter = new IntentFilter();  
  30.         filter.addAction(Intent.ACTION_MEDIA_MOUNTED);  
  31.         filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);  
  32.           
  33.         filter.addDataScheme(“file”);  
  34.           
  35.         registerReceiver(mReceiver, filter);  
  36.          
  37.     }  
  38.       
  39.     @Override  
  40.     protected void onDestroy() {  
  41.         // TODO Auto-generated method stub  
  42.         super.onDestroy();  
  43.         unregisterReceiver(mReceiver);  
  44.     }  
  45. }  

 

修改达成再一次运维程序,不要用BACK退出,按HOME按钮再次回到主荧屏,进入Setting模拟microSD卡插拔,

接下来观察logCat日志监察和控制窗口,神奇的事务爆发了,大家能够收到到事件了!如下图所示:

起名 3

 


3.事件(Intent)的分类:显式 和 隐式

那就是说,为啥加上 filter.addDataScheme(“file”); 就可以了呢?

为了消除这么些题目,大家要先读书Intent的分类。Intent分为两大类,显式和隐式。

 

显式事件,正是指通过 component Name 属性,鲜明钦点了对象组件的轩然大波。

譬如说我们新建2个Intent,指名道姓的说,此事件用于运营名为”com.silenceburn.XXXX”的Activity,那么那正是二个显式事件。

 

隐式事件,就是指没有 component Name 属性,没有明了钦定目的组件的事件。

譬如系统向全部监察和控制通话状态的先后发送的“来电话了!”的风浪,由于系统不鲜明哪个人会处理那些事件,

从而系统不会明显内定指标组件,也便是说没有对象组件,那么那便是个隐式的风波。

 

这里只是简介显式和隐式事件,更标准详细的叙述请查阅SDK文档,

笔者们只须求牢记一点,二种事件的最大分别是 component Name
属性是不是为空。

 


4.轩然大波过滤策略 和 IntentFilter

系统在传递显式事件时尤其有益,因为只要把Intent比作一封信,那么component
Name正是二个详实的收件人地址,

系统能够规范的把显式事件送达目的组件。

 

而传送隐式事件时,就比较麻烦了。因为那封信的封皮上,没有写收信地址!

那如何做吧?系统做了多少个困难的支配,正是把信拆开看看。通过信件内容之中的头脑,去找寻适合的收件人。

比如信中的线索描述到:“收信人是男性,快二十7周岁了,未婚,喜欢玩游戏”,那么系统就在小区中间去找这么的人。

 

非常值得庆幸的事情是,那个小区的人素质万分高,每户人家都写了点自作者介绍在门口,

例如张三写道:“笔者是男性,90后,未婚,喜欢玩游戏”,李四写道:“作者是女性,快三柒周岁了,未婚,喜欢逛街”等等等等。

有了每户人家的自小编介绍,系统就能极快的稳定真正的收件人了!

 

上边是二个类比的例子,不过android系统处理隐式事件的方针,基本上即是上述那种形式了。

 

先是系统会通过观望Intent的内容(打开信件看内容),取得卓越线索,系统所需的线索是之类几种:

 action

 data (both URI and data type)

 category

 

其次,系统中每一种组件,若是想接收隐式事件,则必须表明本身的IntentFilter(自笔者介绍,我对什么样的信件感兴趣)。

至于怎么写IntentFilter,已经卓殊明知道,那就是理所应当是那般写:

“小编是组件XXXX,作者想要接收如此的隐式事件:它的ACTION必须是 XXX,它的
category 必须是 YYYY ,它涵盖的data必须是ZZZZ “

倘若组件不表明IntentFilter,那么全体的隐式事件都不会发送给该零件。(注意,这并不影响向该器件发送显式事件。)

 

对此系统中生出的每一种隐式事件,系统都会尝试将 action, data , category
和种类中逐一零部件注脚的 IntentFilter 去开始展览匹配,

以找到适当的收信人。

 

上述是android系统的事件过滤策略的大致原理,实情远比那要复杂,考虑本文的目标,此处不再进行,SDK文档中都有详细描述。

 


5. 解析TF读取卡插拔事件

由上节亦可,对于显式事件,系统能够准确送达。对于隐式事件,系统一分配析事件的
action, data , category 内容,

并和一一零部件评释的IntentFilter进行匹配,找出相当的零件举办送达。

 

为此icroSD存款和储蓄卡插拔事件是或不是被大家自定义的Recevier收到就取决于如下子难点了:

  1. CF内存卡插拔事件是显式事件,如故隐式事件

  2. SD闪存卡插拔事件的action, data , category 的内容是何等

  3. 大家自定义的Receiver组件的IntentFilter是什么注脚的

 

为了缓解上述1个难点,我们修改一下代码,将CF闪存卡插拔事件的情节打字与印刷到logcat中开始展览察看。

修改后的代码如下:(注意那里我们要添加好 filter.addDataScheme(“file”);
以确认保障事件能够被接到到)

[java] view
plain
copy

  1. package com.silenceburn;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.BroadcastReceiver;  
  5. import android.content.Context;  
  6. import android.content.Intent;  
  7. import android.content.IntentFilter;  
  8. import android.os.Bundle;  
  9. import android.util.Log;  
  10.   
  11. public class SdCardTester extends Activity {  
  12.       
  13.     BroadcastReceiver mReceiver;  
  14.       
  15.     /** Called when the activity is first created. */  
  16.     @Override  
  17.     public void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.main);  
  20.         Log.i(“myLoger”,” onCreate ……”);  
  21.           
  22.         mReceiver = new BroadcastReceiver() {  
  23.             @Override  
  24.             public void onReceive(Context context, Intent intent) {  
  25.                   
  26.                 Log.i(“myLoger”, “Component: ” + intent.getComponent());  
  27.                   
  28.                 Log.i(“myLoger”, “Aciton: ” +  intent.getAction());  
  29.                 Log.i(“myLoger”, “Categories: ” +  intent.getCategories());  
  30.   
  31.                 Log.i(“myLoger”, “Data: ” + intent.getData());  
  32.                 Log.i(“myLoger”, “DataType: ” + intent.getType());  
  33.                 Log.i(“myLoger”, “DataSchema: ” + intent.getScheme());  
  34.                   
  35.                 Log.i(“myLoger”,” Receive SDCard Mount/UnMount!”);  
  36.             }  
  37.         };  
  38.           
  39.         IntentFilter filter = new IntentFilter();  
  40.         filter.addAction(Intent.ACTION_MEDIA_MOUNTED);  
  41.         filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);  
  42.           
  43.         filter.addDataScheme(“file”);  
  44.           
  45.         registerReceiver(mReceiver, filter);  
  46.          
  47.     }  
  48.       
  49.     @Override  
  50.     protected void onDestroy() {  
  51.         // TODO Auto-generated method stub  
  52.         super.onDestroy();  
  53.         unregisterReceiver(mReceiver);  
  54.     }  
  55. }  

 

OK,让大家再一次运维程序,通过Setting模拟插拔TF存款和储蓄卡,观察logCat的输出情状。

下图是挂载SD存款和储蓄卡时的日志输出情状:

起名 4

 

经过日记输出大家得以识破挂载SD闪存卡事件的 Componet
是null ,由此它是多少个隐式事件。

为此能或无法送达,须求看事件的 action, data , category 和
IntentFilter是不是合作。

 

它的ACTION是 android.intent.action.MEDIA_MOUNTED,

和大家定义的IntentFilter的
filter.addAction(Intent.ACTION_MEDIA_MOUNTED); 语句相匹配。

从而action部分是同盟的。

 

它的Categories是null,而小编辈定义的 IntentFilter
也尚未运用addCategory方法增添category定义,

null ==  null,因而 categories也是协作的。

 

action, data , category 中的五个要素已经卓殊,那么是还是不是合营成功的主要,就是看data是或不是匹配了。

 


6. data匹配规则

首先必须认识到,data是二个针锋相对复杂的成分。
data由UHavalI来叙述和一定,UHighlanderI由三局地组成,

 

scheme://host:port/path      模式://主机:端口/路径

 

诸如我们收获的挂载SDXC卡事件,它的data的UOdysseyI是 file:///mnt/sdcard

中间格局部分是 file , 主机:端口部分是空的, path部分是 mnt/sdcard

此外在事件中,仍可以安装data的MIME类型,作为事件的datatype属性。

 

归纳,data是一个较复杂的成分,由此其匹配规则也分外复杂,

 

先是肯定八个匹配原则,就是对于UXC60I的协作,只比较filter中宣称的一部分。

局地匹配原则:只要filter中声称的有个别匹配成功,就以为凡事ULX570I匹配成功。

比方来说,           content://com.silenceburn.SdCardTester:壹仟/mydata/private/

和filter定义为  content://com.silenceburn.SdCardTester:一千/     是足以匹配的。

在意filter中并没有概念path部分,可是依旧得以合营成功,因为filter不注脚的片段不举办比较。

换句话讲,任何符合content://com.silenceburn.SdCardTester:一千/的轩然大波,无论path是怎样,都能够匹配成功。

 

接下去是实在的data部分的,也正是UCR-VI的合作规则如下:

 

1. 只要data的U揽胜极光I和datatype为空,则 filter
的U中华VI和type也无法不为空,才能同盟成功

2. 比方data的UPAJEROI不为空,不过datatype为空,则 filter
必须定义U逍客I并合营成功,且type为空,才能合作成功

3. 假使data的U本田UR-VI为空,不过datatype不为空,则 filter
必须UGL450I为空,定义type并协作成功,才能匹配成功

4. 万一data的U科雷傲I和data都不为空,则 filter
的U途锐I和type都必须定义并合作成功,才能合作成功。

   
对于U奥迪Q5I部分,有贰个特殊处理,就是就算filter没有定义U安德拉I,content和file三种ULANDI也当作既存的U本田CR-VI存在。

   (举个例子,对于 content 和 file
二种形式的data,只要filter定义的datatype能够和事件相当,就以为杰出成功,

     filter不必要显式的增多 content 和 file 三种情势,那二种方式内置协理)

 

有了平整,有了对icroSD存款和储蓄卡插拔事件的剧情的辨析,大家就足以一板一眼照章办事了。


7.CF卡插拔事件的匹配

第叁如第5节所述,microSD卡插拔是三个隐式事件,而且 action 和 category
部分和我们的 filter 均能够包容成功。

 

其data部分的URI为 file:///mnt/sdcard ,datatype为 null
,因而选用第肆节相比较规则中的 2 号规则:

    2. 只要data的U劲客I不为空,不过datatype为空,则 filter
必须定义ULacrosseI并同盟成功,且type为空,才能匹配成功

 

咱俩的filter中从不运用 addtype 方法 ,因此 filter 的type为空,
datatype部分匹配成功。

data的URI为file:///mnt/sdcard ,大家选择 filter.addDataScheme(“file”);
语句定义 schema 为 file,

基于一些匹配规则,data匹配成功。

 

迄今甘休,整个事件极度成功,至此,大家就知晓了文初的题目,为啥必须添加  filter.addDataScheme(“file”);
语句才能接收事件!

 

咱俩得以尝尝把 filter.addDataScheme(“file”); 后边扩充语句

[c-sharp] view
plain
copy

  1. filter.addDataPath(“mnt/sdcard”, PatternMatcher.PATTERN_LITERAL);  

反之亦然能够匹配成功,收到microSD卡插拔事件。因为这么就结成了八个URI的完全匹配。

 

咱俩得以尝尝把给 filter 增添 datatype 属性,

[java] view
plain
copy

  1. try {  
  2.     filter.addDataType(“text/*”);  
  3. } catch (MalformedMimeTypeException e) {  
  4.     // TODO Auto-generated catch block  
  5.     e.printStackTrace();  
  6. }  

诸如此类就不恐怕合作成功了。因为SD读取卡插拔事件的datatype是null,

而作者辈定义的filter的datatype是MIME”text/*” 。

 


总结

从那之后文初的题目分析完结。老生常谈,事实上android平台的intentFilter处理机制远远复杂于本文所述范围。

越发是本文对action和category三种因素的研讨分外少大致从未,实际上那三种成分的处理也是比较复杂的。

 

本文只是冰山一角的讲述了了一些基本原理和基本准则。

IntentFilter机制作为android平台的重中之重基础知识之一,大家我们要一同接二连三努力学习,和豪门共勉
🙂

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图