WDP在设计时就以UDP作为参考;实际编程中,如果底层已经实现IP层连接,比如GPRS/CSD类的连接,则直接使用UDP的接口编程即可。其它类型的连接(SMS/USSD等),需要实现这些底层转换。
参考文档:WAP-WDP-reference
WTP层实际上是一种轻量级的事务处理层,通过TransactionID的机制和“请求-响应”机制确保消息传递的一致性和可靠性。
WTP层有三种类别,分别为Class0/Class1/Class2,分别表示不同的可靠性机制,我们只讨论Class2类别。
客户端和服务器端之间使用的Class不能协商,只能由发起者指定。
每一次浏览操作的事务都是由TID来保证的。在WTP层数据包中,大部分都包含TID字段。TID由两个字节组成,第0位是标记位,用于标记方向:发起者标记为0,响应者标记为1
Initiator: 0x00, 0x01
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
Responder: 0x80, 0x01
1 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
WTP PDU类型
WTP层有以下几种消息:
▲ Invoke message;
▲ Verification
▲ Hold on acknowledgement
▲ Result message;
▲ Last acknowledgement;
每种消息都是由一个或多个PDU组成。一个PDU是一个最小的协议数据单元。WTP层所有的PDU类型如下:
名称 |
值 |
(禁止使用) |
0x00 |
Invoke |
0x01 |
Result |
0x02 |
Ack |
0x03 |
Abort |
0x04 |
Segmented Invoke |
0x05 |
Segmented Result |
0x06 |
Negative Ack |
0x07 |
▲ Invoke PDU
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
PDU Type=Invoke |
GTR |
TTR |
RID |
|||
2 |
TID(Transaction ID) |
|||||||
3 |
||||||||
4 |
Version |
TIDNew |
U/P |
RES |
RES |
TCL |
▲ Result PDU
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
PDU Type=Result |
GTR |
TTR |
RID |
|||
2 |
TID(Transaction ID) |
|||||||
3 |
▲ Acknowledgement PDU
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
PDU Type=Ack |
Tve/Tok |
RES |
RID |
|||
2 |
TID(Transaction ID) |
|||||||
3 |
▲ Abort PDU
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
PDU Type=Invoke |
Abort Type |
|||||
2 |
TID(Transaction ID) |
|||||||
3 |
||||||||
4 |
Abort reason |
其它PDU不做介绍,上述一些位的具体含义,请参考相关规范。蓝色粗体部分都是比较主要的内容。
以下描述的基本流程主要应用于WTP Class2类别。
任何一个事务操作,都以Invoke消息作为初始消息;如果响应者接收到Invoke消息,并且发现该TID无法在缓存中匹配,则需要开始“三步握手”流程,即红色标记部分:发送Ack消息,询问发起者改Tid是否有效;发起者则回应一个Ack消息,说明该Tid是否有效。如果返回说明Tid无效,则响应者必须Abort此次事务(本图未标记这个Abort流程);如果Tid有效,则以后的事务操作均需携带这个Tid,直到这个事务结束。
WAP协议栈深入分析
WDP相当于UDP。这一层需要考虑的问题是UDP缺乏流量控制机制。在互联网上,一般网卡的MTU上限都是1500左右。所以当单个WDP数据包大于1500时,可能会被部分防火墙拒绝,或者被丢弃过长部分。因此必须在WTP层进行WDP数据包分解和重组(Segmented And Assemble),下节会详细描述。
在WAP协议规范中,一般WAP网关在UDP层默认接收的最大数据包长度为1400,超过这个长度一般被拒绝,除非在WSP层进行Capabilities协商。
在前面的文章中描述了WTP层的基本交互流程和协议字段。这种流程是最基本、最简单的流程,仅适用于数据包<1400的情况。当传送的数据包有可能突破这个上限时(包括发起者和响应者),就必须采用分节和重组的机制。
WTP PDU结构中,头字段包括以下两位:
GTR |
TTR |
Desc |
0 |
0 |
不是最后一节 |
0 |
1 |
消息包的最后一节 |
1 |
0 |
分组包的最后一节 |
1 |
1 |
不支持分节重组 |
前面描述的基本流程就是GTR=1, TTR=1的情况,即不支持分节重组机制。如果支持分节重组机制,必须采用前三种状态(00,01,10)。在分节重组机制下,除了前面已介绍的WTP PDU,还需要了解以下三种专为分节重组机制使用的PDU:
▲ Segmented Invoke PDU
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
Type=Segmented Invoke |
GTR |
TTR |
RID |
|||
2 |
TID(Transaction ID) |
|||||||
3 |
||||||||
4 |
Packet Sequence Number (PSN) |
▲ Segmented Result PDU
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
Type=Segmented Result |
GTR |
TTR |
RID |
|||
2 |
TID(Transaction ID) |
|||||||
3 |
||||||||
4 |
Packet Sequence Number (PSN) |
▲ Negative Ack PDU(当丢失某分节时通知对端重传)
Bit/Octet |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
CON |
Type=Negative Ack |
Reserved |
RID |
||||
2 |
TID(Transaction ID) |
|||||||
3 |
||||||||
4 |
Number of Missing Packets = N |
|||||||
5 |
Packet Sequence Number(s) of Missing Packets |
|||||||
… |
||||||||
4+N |
采用分节重组机制的交互流程详细描述如下:
◆ 首先发送普通的Invoke PDU,在此PDU中,GTR/TTR根据数据包的情况设置。当数据包<1400时,则赋值:GTR=0/TTR=1,表示这是消息的最后一节;当数据包>1400时,则拆分成多个分节,每个小于1400,第一个PDU为普通Invoke PDU,其后的都为Segmented InvokePDU,而且除最后一个PDU的GTR/TTR设置为01,其它的为00;另外,需要注意的是每个分节必须按照顺序(PSN)来分解,以便对端能按顺序重组;
◆ 对端收到数据包,处理结束后,返回的Result PDU也是一样情况。即第一个PDU为普通Result PDU,GTR/TTR根据是否最后一个分节来设置是00还是01;紧接着的PDU均为Segmented Result PDU,GTR/TTR根据分节情况设置;当把所有包都收集完整后,要剔除包头,把所有数据按顺序(PSN)进行重组;
◆ 分节重组机制中,还有一个分组的概念。即一个消息被拆分成一个或多个分组发送,每个分组再分成多个分节发送;分节发送作为最小的发送单元,分组发送作为可选方式,用于对超大数据传送提供质量保证。
◆ 当任何一端接收到一个分组结束的标记(GTR/TTR=10),系统应自动返回响应Ack PDU,通知对端分组接收成功。此处的Ack PDU即为普通的Ack PDU,GTR/TTR设置为01,同时设置最高位CON=1,在头部后增加可变的TPI部分,此时的TPI是Packet Sequence Number TPI,把当前接收的分组PSN响应给对端;(PSN TPI结构,请参考WTP 9.4.5节);
◆ 此外,还必须接收对端对Tid的验证请求,并进行响应,与前面章节描述的流程一样;
图示流程如下: