最近因为某些项目的原因,需要实现WapPush的功能,本想偷懒在网上搜索一下,看有没有合适的内容,结果找不到一个合适的.只好自已动手来写一个.
看了一下PAP协议的内容,其实也并不复杂,属于HTTP协议的扩展,因此,使用.NET的HttpWebRequest类库便可以方便的实现PAP的通讯层.剩下的工作便是PAP协议内容的组合了,PAP协议主要有四个方法及一个交互方法,分别是:
- Submit a Push 提交一个Push信息
- Cancel a Push 取消一个Push信息
- Query for status of a Push 查询提交Push信息的状态
- Query for wireless device capabilities 查询无线设备能力
如果需要接收PPG(推送代理网关)返回的发送结果通知消息,则需要提供一个回调地址,供PPG发送通知响应.PI(推送发起人)与PPG之间的关系如下图:
PI端与PPG之间使用PAP协议通讯,PPG则调用空中协议传送到手机端.
上图则是PI发送一个WapPush信息,PI与PPG之间的交互过程
- PI发起一个推送请求
- PPG返回一个接收响应包
- PPG推送WapPush到手机终端
- 手机终端响应接收结果
- PPG推最终发送结果到PI端
- PI端返回一个响应包到PPG,完成整个发送过程
对于Cancel a Push, Query for status of a Push , Query for wireless device capabilities 这三种方式,都是PI与PPG之间的交互,即发起请求,得到响应的方式,因此,不作详细说明.
消息体格式
PAP协议的消息体是采用多段的方式提交请求的,即Content-Type类型为"multipart/related",到于具体的信息,可以参考RFC2387.整个PAP协议包包括三种结构,分别为
- 控制实体(Control Entity):提供一些控制命令
- 内容实体(Content Entity):提供传送的内容信息
- 能力实体(Capabilities Entity):提供终端的支持的能力配置信息
除了推送消息需要使用到三种消息以外,其它请求方法都只使用到控制实体包。这三种类型的包都是MIME类型格式,下面是一个请求包的具体格式
Content-Type: multipart/related; boundary=asdlfkjiurwghasf;
type="application/xml"
--asdlfkjiurwghasf
Content-Type: application/xml
<?xml version="1.0"?>
<!DOCTYPE pap PUBLIC "-//WAPFORUM//DTD PAP 2.0//EN"
"http://www.wapforum.org/DTD/pap_2.0.dtd"
[<?wap-pap-ver supported-versions="2.0,1.*"?>]>
<pap>
..control for PPG..
</pap>
--asdlfkjiurwghasf
Content-Type: text/vnd.wap.si
..Service Indication push message example..
--asdlfkjiurwghasf
Content-Type: application/xml
..assumed client capabilities..
--asdlfkjiurwghasf--
控制实体介绍
控制实体的定义如下
<!ELEMENT pap ( push-message
| push-response
| cancel-message
| cancel-response
| resultnotification-message
| resultnotification-response
| statusquery-message
| statusquery-response
| ccq-message
| ccq-response
| badmessage-response) >
<!ATTLIST pap
product-name CDATA #IMPLIED
>
它一共支持11种控制命令,这些命令都必须放在PAP节点下面,如:
<pap>
<push-message ......../>
</pap>
支持的11种控制命令分别如下:
push-message :PI -> PPG ,发起一个Push请求
push-response :PPG -> PI,PPG对Push请求的响应结果
cancel-message :PI->PPG,取消消息的请求
cancel-response :PPG -> PI,PPG对取消请求的响应结果
resultnotification-message :PPG->PI,结果提醒消息,由PPG调用通过PUSH消息时提供的通知地址传送
resultnotification-response:PI->PPG,PI端对PPG的结果提醒消息的响应
statusquery-message :PI->PPG,状态查询消息,查询发送消息的当前状态
statusquery-response :PPG->PI,PPG对查询请求的响应结果
ccq-message :PI->PPG,发起一个终端能力查询请求
ccq-response :PPG->PI,PPG对终端能力查询的响应结果
badmessage-response:PPG->PI,PPG在接收到错误请求包的时候,返回的响应信息
push-message介绍
结构定义如下:
<!ELEMENT push-message ( address+, quality-of-service? ) >
<!ATTLIST push-message
push-id CDATA #REQUIRED
replace-push-id CDATA #IMPLIED
replace-method ( pending-only | all ) "all"
deliver-before-timestamp %Datetime; #IMPLIED
deliver-after-timestamp %Datetime; #IMPLIED
source-reference CDATA #IMPLIED
ppg-notify-requested-to CDATA #IMPLIED
progress-notes-requested ( true | false ) "false"
>
<!ELEMENT address EMPTY >
<!ATTLIST address
address-value CDATA #REQUIRED
>
<!ELEMENT quality-of-service EMPTY >
<!ATTLIST quality-of-service
priority ( high | medium | low ) "medium"
delivery-method ( confirmed | preferconfirmed
| unconfirmed | notspecified ) "notspecified"
network CDATA #IMPLIED
network-required ( true | false ) "false"
bearer CDATA #IMPLIED
bearer-required ( true | false ) "false"
>
push-message节点是推送一个消息的控制节点,它有多个属性,含义如下:
push-id:推送流水ID,由PI端创建管理,需要全局唯一,返回响应包、通知包会根据此ID来判断请求
replace-push-id:替换推送ID,指被替换的旧PushID,多用于使用新的消息替换以提交却未发送的消息
replace-method:替换的方法,有pending-only和all两种选项可选,all表示替换旧消息的所有接收者,pending-only则表示替换有可能被取消的接收者
deliver-before-timestamp:发送终止时间,指超过指定的时间则不发送消息了,格式为标准的UTC时间:YYYY-MM-DDThh:mm:ssZ
deliver-after-timestamp:发送的开始时间,须在指定的时间才能开始发送,格式为标准的UTC时间:YYYY-MM-DDThh:mm:ssZ
source-reference:内容提供者的文本名称,PPG网关可能需要此参数判断PI的权限及能力
ppg-notify-requested-to:PPG发送结果响应请求的Url地址
progress-notes-requested:是否在响应包中包括处理日志信息
address节点需要包括在push-message节点下,如果有多个接收人,则增加多个address节点即可,address-value是实际的接收地址,它可以是一个逻辑地址,具体的格式大致如下:
wappush=12345678/type=PLMN@ppg.operator.com
quality-of-service节点也须要放在push-message节点下,它描述了当前消息的各种发送属性,如:优先级、使用网络、承载体等,不同的网络或许有不同的参数,请咨询相应的运营商。
具体的参数说明可以参考标准文档:wap-247
push-response介绍
<!ELEMENT push-response ( progress-note*, response-result ) >
<!ATTLIST push-response
push-id CDATA #REQUIRED
sender-address CDATA #IMPLIED
sender-name CDATA #IMPLIED
reply-time %Datetime; #IMPLIED
>
push-response是PI提交push-message到PPG后,PPG返回的响应包结构,它有四个属性,如下:
push-id:相关的push-id
sender-address:发送者地址,一般是PPG的地址
sender-name:发送者名称,一般是PPG的名称
reply-time:应答时间,标准的UTC时间
progress-note节点不多介绍,是属于处理日志节点
response-result节点包含了所发送消息的响应结果,其定义如下:
<!ELEMENT response-result EMPTY >
<!ATTLIST response-result
code CDATA #REQUIRED
desc CDATA #IMPLIED
>
code是响应的结果,具体可参考标准文档的说明,desc则是相应的描述信息
对于其它几个请求结构相对简单,此处不再说明,参考标准文档即可。
HTTP请求处理说明
PAP协议是基于HTTP协议,因此,建立一个PAP协议只需要发起一个HTTP请求即可,截取一段发送代码如下:
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
//Pap请求
HttpWebRequest papRequest = (HttpWebRequest)HttpWebRequest.Create(Config.ServiceUrl);
papRequest.Method = "POST";
papRequest.ContentType = "multipart/related; type=application/xml;boundary=" + IRequest.Boundary;
papRequest.Headers.Add("Authorization", Config.EncPass);
byte[] papData = rqeuestObj.GetBytes(Config.enc);
Stream requestStream = papRequest.GetRequestStream();
requestStream.Write(papData, 0, papData.Length);
requestStream.Close();
HttpWebResponse response = (HttpWebResponse)papRequest.GetResponse();
StreamReader sr = new StreamReader(response.GetResponseStream(), Config.enc);
ret = ReplaceXml(sr.ReadToEnd());
sr.Close();
主要设置几个属性,分别为Method、ContentType以及Authorization,对于前两个不作说明,Authorization则表示认证信息,有些PPG网关不需要认证信息,因此可以忽略这个属性。认证属性的值为Base64编码的字串,格式为:Base base64String
用户名和密码以这样的格式编写:UserName:Password,然后对这个串进行编码,放在Base 后面即可。表示基本的Base64认证串。对于其它的代码就不多做解释了:)大家可以看源码。
关于源码:
目前仅实现了PAP协议的解析、生成等功能,未实现对数据发送的队列控制、流量控制等等,这些都需要进一步完善,如果各位有兴趣完善的话,希望也提交到项目里面咯:)
下载地址:http://paplib.codeplex.com/