2009年8月22日星期六

与 cmwap 斗争

前天早晨在推上 @zaykl 问我可否走 cmwap 使用飞信 api 。我折腾了这三天,总算是有个结果了。回答是肯定的,完全可以。
移动 wap 网关是大坏蛋。我们发送访问某 url 的请求,移动总会拦截第一次请求,可能返回一个可解析的 wml 页面,其中包含所请求页面 url 加一个尾巴构成的跳转地址;也可能是返回一串伪·乱码数据。根据我的不完全归纳,这种数据以 "\x01\x01\x01\x01" 结尾,不支持按 utf-8 解码。从 print 出来的内容上看,疑似和 wap 缓存机制有关。二者状态代码都是 200 成功,但其实都没有成功。第二次及之后的请求才能真正传到目的服务器上去。
具体到通过 wap 使用飞信 api 这件事,服务器返回的状态可能是 200 成功(但返回的字符串可能告诉你发送成功、没发出去、没加好友、密码错误等等)或者 302 重定向。
据说只有 s60 会有返回伪·乱码的情形发生、自动处理 302 重定向也存在问题, s40 则都没问题。以我个人的经历,前天几乎没碰上伪·乱码,碰上只以为是偶尔抽风,昨天渐渐多起来,今天又忽而连续出现好几次,忽而神隐。但没见大家讨论出个所以然,毕竟这不仅跟手机终端有关,能跟天朝移动 wap 网关的设备、设置扯上关系,多么 RP 也都是正常。
搜到的思路一般是把必然会被拦截的第一次访问作为初始化的一部份,之后怎么调用都应该正常返回。
我写的方法是检测返回的数据,发现是以 xml 声明开头或者以 "\x01\x01\x01\x01" 结尾就再请求一遍。
这里给出的是我的测试代码,没有封装,只是实践一下,证明其可行性。
⊙u⊙代码开始⊙u⊙
import httplib
import socket
apo=socket.access_point(8)
socket.set_default_access_point(apo)
# 我的手机上 cmwap 接入点 id 是 8 ,这么写方便~
# 具体手机上接入点 id 可以用 socket.access_points 来看,也可以参考金色葡萄的源代码来看如何设置和保存接入点。
conn = httplib.HTTPConnection("10.0.0.172",80) # 连接移动的 wap 网关
url=r'https://fetionapi.appspot.com/api/?from=13888888888&pw=xxxxxxxx&to=13999999999&msg=test'
# 具体数据请换成可以用的手机号、飞信密码、飞信好友的手机号及要发的信息
# 用户名、密码的保存、中文信息的发送请继续参考金色葡萄XD
# 用 https 请求比较好。我绕了很令人吐血的弯路,原因在下面说。
conn.request('GET',url)
r=conn.getresponse()
s0=r.read()
print r.status
if s0.startswith("<?xml version=") or s0.endswith("\x01\x01\x01\x01"): # 是移动劫持返回的数据
# s0.endswith("\x01\x01\x01\x01") 这个判断条件很可能是不对滴~但我也没碰上反例,有人碰上的话麻烦一定过来吱一声啊~
print s0
conn.request('GET',url)
r=conn.getresponse()
print r.status
s=r.read()
print "Request again.",s.decode("utf-8")
else: # 道路已经打通,是从 fetionapi.appspot.com 取得的返回数据
print s0.decode("utf-8")
conn.close()
apo.stop()
⊙u⊙代码结束⊙u⊙
测试环境: 诺基亚 N72 ,python s60 1.44 ,浙江移动神州行 cmwap 接入点。
=}=}=}=}废话(笔记/心得)开始的分割线,没兴趣的可以 goto 页面底端=}=}=}=}
我包月的流量不分接入点,几乎从不用 wap 。幸好反编译过 pys60 时间同步程序(作者是乐迅 PY 软件开发区的"√ゞ絕配ぷ无恋℡")的源代码,见过一点使用 cmwap 接入点的例子,折腾起来还有点底。开头的那段基本都是抄的。
=}=}=}=}一觉睡了18个小时,时间偷偷溜走的分割线=}=}=}=}
折腾良久,才知道移动会在第一次访问网页的时候劫持请求,转到确认要收钱的页面上去,里面的链接通向请求的页面,米有牙齿的移动还在链接的后面加了个 ?t= 随机数字串的尾巴。搜到有人说再次请求就 ok ,可我这里直就 302 错误了……而对 http 协议基本一窍不通的我甚至连重定向后的地址应该在哪里取都没概念~囧
又耽搁了好久才想起有 dir 这么个宝贝,把返回的那堆东西对着 httplib 的源代码一个属性一个属性的看。总算找到了~
※※见招拆招的一点一点跳转的代码开始※※
# 此处的代码最后都没有用到
# 因为是从废弃的代码尸体上一块一块摘过来的,所以也不保证能够正常运行,只是作为思路的记录和参考
# 移动拦截请求返回的页面有两种,我只处理了其中一种不是乱码的,所以这个思路有错误
def c(convert_str):
# 从移动的xml里提取跳转的url时用到,把特殊符号转回原来的样子
convert_str = convert_str.replace('&amp;','&')
convert_str = convert_str.replace('&quot;','"')
convert_str = convert_str.replace('&apos;',"'")
convert_str = convert_str.replace('&lt;','<')
convert_str = convert_str.replace('&gt;','>')
return convert_str

# 上接第一次请求
if r.status==200:
# 如果请求成功
# 这里只处理了劫持返回的 wml ,因为当时伪·乱码几乎没出现过
# 有效 url 在" <go href="xxxxxxxx"></go> "的 "xxxxxxxx" 位置
s=r.read()
newadr=c(s[s.find("<go href=")+10:s.rfind("></go>")-1])
# 返回需要访问的网址
# 遇到伪·乱码就会返回空字符串,后面就出错了(>_<)
conn.request('GET',newadr) # 访问之
r=conn.getresponse()
※※一点一点跳转的代码暂停※※
=}=}=}=}被 302 弄得焦头烂额的分割线=}=}=}=}
不知啥时候我爪贱的删除了 "https" 的 "s" ,在开始的 url 中用了 http 请求,太失策了!拦截页面提取出来的 url 是 http://fetionapi.appspot.com:80/...&t=xxxxx ,访问这个 url 会出现 302 重定向到 https://fetionapi.appspot.com/...&t=xxxxx ,再访问它,不知为啥会取不到返回的 字符串"Yes :) 成功发送!",非常不爽啊~
从一开始就用 https 的话,我就没碰上过 302 重定向的问题。
※※一点一点跳转代码继续:使用 http 请求时遇上 302 重定向的处理代码※※
if r.status==302:
print r.getheader("Location")
conn.request('GET', r.getheader("Location"))
# 不可以用 r=conn.getresponse() ,因为会引发 "ResponseNotReady" 的异常。
# 于是也取不到返回的字符串,无法知道飞信到底发出去没,讨厌死了!
※※一点一点跳转的代码摘录到此为止※※
开始的时候是把"一点一点跳转"的这些代码用 while 语句弄在一起,不管你跳转一次两次还是三次,一直跳到返回的函数以 "Yes" 开头为止。
随着伪·乱码的频频出现,我只好放弃这个思路,被移动劫持了直接重发。就像在开头写的那样。
=}=}=}=}只懂得开场不懂得终场的分割线=}=}=}=}
知道 conn.close() 也知道 apo.stop() ,但是单用哪个都不不能及时关闭连接。我果然够笨~两个连起来用似乎不管什么顺序都可以关闭连接。
从萝莉, sorry 是逻辑上说似乎应该是先 conn.close() 再 apo.stop() ?那就这个样子好了。
=}=}=}=}忆苦思甜的分割线=}=}=}=}
说实话,用 cmnet 接入点的时候,我是直接用了 urllib.urlopen 来实现的,所以完全没想到用 httplib 和 socket 会这么复杂(>_<)
§§附送我自己用的走 cmnet 的飞信函数§§
def fetion(number,msg,mytel,pw):
apo=socket.access_point(3)
# 这个 3 是我的手机 cmnet 的接入点 ID ,就像开头的 8 一样。
apo.start()
url="""https://fetionapi.appspot.com/api/?from="""+mytel+"&pw="+pw+"&to="+number+'&msg='+urllib.quote(msg.encode("utf-8"))
answer=urllib.urlopen(url.encode("utf-8"))
apo.stop()
appuifw.note(answer.read().decode("utf-8"),'conf',1)
§§搞定!简单吧§§
如果给自己发,稍等片刻短信就来了。
=}=}=}=}笔记/心得结束、闲话开始的分割线=}=}=}=}
今天和同事换班,在家里继续休息了一整天。于是连续的三个休息日除了吃饭睡觉就解决了这么一个问题~飘浮。
不过收获的确蛮大的。起码心理上不害怕 http 了~囧
认真码字才发现自己语言表达能力退化严重。泪奔~
这次的人生体验:迷路时能看到更多的风景。
=}=}=}=}废话(笔记/心得)彻底结束的分割线=}=}=}=}

参考资料:
http://goldengrape.org/2009/05/fetion_for_the_one/ 给同一个人的飞信
http://careerman.blog.ccidnet.com/blog-htm-itemid-2483039-uid-12026-do-showone-type-blog.html symbian cmwap 接入点 小结
http://topic.csdn.net/u/20080510/20/e0696874-b3f7-4dad-80ea-09e591854165.html 使用cmwap方式访问网络,返回的数据是乱码?
http://topic.csdn.net/u/20070810/11/02c1f8f7-23b9-4307-8de6-2cae07a92e0d.html [讨论]nokia S60用cmwap连接网络,好像经常数据被移动替换了
http://www.zj.monternet.com/download/zj/wap_faq.doc 浙江移动WAP技术FAQ

没有评论:

发表评论