C# WINCE彩信

有个项目涉及到彩信的收发, 于是研究了下wm上面该如何收发彩信, 得到的结论令人非常的遗憾, 系统没有提供这样的直接的接口, 也很难找到变通的办法, 因为在wm平台上, 彩信程序是由硬件厂商来实现的.
也就是说我不得不自己从头到尾的来实现彩信的收发.

一 WM上彩信的发送

彩信是建立在wap协议之上的一种应用, 所以要发送彩信首先要在设备上建立wap连接.(是不是一定要建立wap连接, 是不是一定要走wap协议, 实际上这取决于网络服务商的wap网关和彩信服务器, 比如广州移动在前段时间是支持http发送彩信的, 目前的广州联通支持net网络下wsp发送彩信).
但是为了保证我们的程序能够更具有通用性, 最好是选择wap连接, 并且走wap协议用wsp发送.


1 http方式发送
原则上, 只要网关支持wap2.0协议, 就可以用http发送的. 用http发送时, 先与网关建立tcp/ip连接(目前移动和联通的http网关都是10.0.0.172:80), 然后发送一个post请求.下面便是一个在广州移动发送成功的post请求.(现在不知道为什么发不成功了)
//MmscName为彩信服务器, 移动为”http://mmsc.monternet.com“, 联通为”http://mmsc.myuni.com.cn
string headerBuffer = “POST http://” + MmscName + ” /HTTP /1.1rn”;
headerBuffer += “Accept: image/png, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*rn”;
//网关地址, 目前移动联通都是 10.0.0.172:80
headerBuffer += “Host: 10.0.0.172:80rn”;
headerBuffer += “X-Online-Host: ” + MmscName + “rn”;
headerBuffer += “Pragma: no-cachern”;
headerBuffer += “Cache-Control: no-cachern”;
headerBuffer += “Connection: Keep-Alivern”;
headerBuffer += “Accept-Encoding: deflate, gziprn”;
headerBuffer += “User-Agent: SAMSUNG-SGH-E908/NetFront 3.2/WAP2.0 Profile/MIDP-2.0 Configuration/CLDC-1.1rn”;
headerBuffer += “Accept: application/vnd.wap.mms-message, image/vnd.wap.wbmp, image/png, image/jpeg, image/gif, text/x-iMelody, text/x-imelody, application/x-midi, audio/midi, audio/mid, audio/x-mid, image/bmp, audio/mp3, audio/x-midi, audio/amr, application/vnd.smaf, application/vnd.wap.mms-message x-wap-profile:http://wap.samsungmobile.com/uaprof/e908_10.xmlrn”;
//s_length 为彩信包的长度
headerBuffer += “Content-Length:” + s_length + “rn”;
headerBuffer += “Content-Type:application/vnd.wap.mms-messagernrn”;
//在rnrn之后接的就是彩信包数据了
发送完成之后, 等待网关的响应, 返回200 ok, 表明彩信发送成功, 否则就是发送失败. 失败的原因有可能是http头组错了, 也可能是mms包错了, 但更可能的原因还是网关不支持.
2 wsp方式发送
wsp的发送要复杂一些, 需要了解wap wtp wsp 这一套协议. WAP-230-WSP-20010705-a, WAP-224-WTP-20010710-a 这两份文档中有详细的描述.
wsp方式发送完成之后, 网关也会返回结果, 但是有时候解析出来的结果表明没有发送成功, 但是对方却正确的收到了彩信.不知道是wsp发送有问题, 还是mms包组得有问题.
使用wsp方式放送时, 使用的网关端口是1920.
3 mms的打包
mms有两种打包方式, 比较简单的是application/vnd.wap.multipart.mixed, 复杂一点的是 application/vnd.wap.multipart.related. 我的彩信程序实际上是要发送一个附件文件给对方, 所以只需要application/vnd.wap.multipart.mixed方式就可以了,所以我只研究了这种简单的方式.
使用application/vnd.wap.multipart.mixed方式组装的MM 顾名思义是所有的消息内容混合在一起没有时间上的顺序,所有的消息内容MMSContent所指向的之间是没有任何关系的如果说有关系的话就是存放在MM 中的时间上的先后但是在显示这些消息内容的时候可能就没有时间上的之后很有可能就是一次显示出来.
这种方式只需要填写一些主要的关键字, 然后接上彩信附件数据就可以了.
下面是一个简单的mms包, 打包过程
//X-Mms-Message-Type(0x8c): m-send-req(0x80)
BYTE* pCurPos = pBuffContent;
*pCurPos = 0x8c; pCurPos++;
*pCurPos = 0x80; pCurPos++;

//ID
*pCurPos = 0x98; pCurPos++;
char* pID = NULL;
GetID( &pID );
strcpy( (char*)pCurPos, pID );
pCurPos += strlen( pID);
*pCurPos = 0x00; pCurPos++;

//X-Mms-MMS-Version(0x8d):v1.0(0x90)
*pCurPos = 0x8d; pCurPos++;
*pCurPos = 0x90; pCurPos++;

//from
*pCurPos = 0x89; pCurPos++;
*pCurPos = 0x01; pCurPos++;
*pCurPos = 0x81; pCurPos++;

//To(0x97)
TCHAR szToCombin[50];
_stprintf( szToCombin, _T(“%s%s%s”), _T(“+86”), szTo,_T(“/TYPE=PLMN”) );
*pCurPos = 0x97; pCurPos++;
char* pTo = NULL;
String_WCharToMByte( szToCombin, &pTo );
strcpy( (char*)pCurPos, pTo );
pCurPos += strlen(pTo);
*pCurPos = 0x00; pCurPos++;

//subject
*pCurPos = 0x96; pCurPos++;
char* pSubject = NULL;
String_WCharToMByte( szSubject, &pSubject );
//pSubject = “MmsTest”;
strcpy( (char*)pCurPos, pSubject );
pCurPos += strlen(pSubject);
*pCurPos = 0x00; pCurPos++;

//Content-Type: application/vnd.wap.multipart.mixed
*pCurPos = 0x84; pCurPos++;
*pCurPos = 0xa3; pCurPos++;

//multipart,count
*pCurPos = 0x01; pCurPos++;

//headerslen
*pCurPos = 0x0f; pCurPos++;

//附件数据
char* pAttachData = NULL;
String_WCharToMByte( szXmlData, &pAttachData );

//DataLen, 附件数据长度
int iDataTextLen = strlen(pAttachData) + 1;
int iByteLen = 0;
BYTE* pDataLen = encodeUintvar( iDataTextLen, iByteLen );
strncpy( (char*)pCurPos, (char*)pDataLen, iByteLen );
pCurPos += iByteLen;
//*pCurPos = (unsigned char)iDataTextLen; pCurPos++;

//headlen
*pCurPos = 5 + _tcslen(szXmlFileName); pCurPos++;

//uft-8
*pCurPos = 0x83; pCurPos++;
*pCurPos = 0x85; pCurPos++;

//attach file name
char* pAttachFile = NULL;
String_WCharToMByte( szXmlFileName, &pAttachFile );
//pAttachFile = “phone.xml”;
strcpy( (char*)pCurPos, pAttachFile );
pCurPos += strlen(pAttachFile);
*pCurPos = 0x00; pCurPos++;

//Charset utf-8
*pCurPos = 0x81; pCurPos++;
*pCurPos = 0xea; pCurPos++;

//attach file content
strcpy( (char*)pCurPos, pAttachData );
pCurPos += strlen( pAttachData );
*pCurPos = 0x00; pCurPos++;

//彩信包实际长度
iRealLen = pCurPos – pBuffContent;

二 WM上彩信的接收

个人认为, 在Windows Mobile平台上彩信的接收比发送更加麻烦. 因为拦截彩信通知很麻烦, 还得与系统自带的彩信程序抢时间, 因为如果系统自带的彩信程序接收完彩信之后回了ack, 这时我还没有接收的话那我就收不到了.
1 彩信通知的拦截.
彩信通知实际上是一条特殊的短信, 在系统的收件箱肯定是看不到得了, 也就是说用普通的mapi从收件箱获取这条彩信通知短信肯定是不行的了.
我尝试了用mapirule拦截短信的方式来拦截, 发现这条通知短信压根就没到那个地方.
在网上查到资料有一套api(PushRouter_RegisterClient)是专门用来拦截push消息的, sdk下还有个sample, 但是我试了两天, 始终没有成功.
后来用RIL, 拦截RIL_NOTIFY_MESSAGE消息, 终于拦到了, 不过总觉得这个办法不太好.
彩信通知实际上一段压缩二进制编码数据, 在这段数据中包含了彩信发送方, 主题, 以及url. 这个url实际上就是彩信的存放地址. 解析这段二进制编码获取到url就可以去取彩信了.
2 彩信的获取
彩信的获取也需要与wap网关打交道, 自然也需要先建立wap网络连接, 然后与网关(也是192.168.0.172:80) 建立tcp/ip连接, 向网关发送一个get请求就可以了.
char szGetString[MAX_PATH] = “”;   //pUrlMbyte 就是上一步获取到的url彩信地址
sprintf( szGetString, “GET %s HTTP/1.1rnrn”, pUrlMByte );
网关会给这个get请求响应, 如果发送成功, 会得到如下的响应
HTTP/1.1 200 ok
Content-Type: application/vnd.wap.mms-message
Content-Length: 1301
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Keep-Alive: timeout=15, max=49
Via: HTTP/1.1 SDJN-PS-WAP2-GW07(infoX-WISG, Huawei Technologies)
Server: mmsc-jn4-rsv.monternet.com
x-mmsc-msg-from: mm1

???9kN4I0hTkVR0???062117015591007101190? J=?+? ?+8613207008167/TYPE=PLMN?8615206300005/TYPE=PLMN? ? <RDWNPB id=’3063′ />??????? ??<start>?application/smil ?l ???<PHONEBOOK> <NODE name=”¥?????¥??(¥??)” cont=”5551012
上面的那段明文就是http头,看到200 ok就表示接收成功了, 在两个换行符的后面就是mms数据了, 取出这部分数据进行解析就是了.
3 到这里并没有完, 还要给彩信服务器回一个ack, 不然彩信服务器会认为我们没有收到彩信, 会再次发彩信通知过来.
回ack也是post一个请求到网关.

点赞

发表评论