« May 2009 | Main | July 2009 »

June 28, 2009

玩了一下 ActionScript

周末。

玩了一下 ActionScript 。因为感觉做一些简单的需要长连接的互联网应用,flash 是一个不错的选择。在大多数情况下,比要求用户安装一个客户端要人性。(当然,和要求用户为浏览器安装一个莫名其妙的 ActiveX 控件相比,让用户自己决定是否下载独立客户端要友好的多)

因为,虽然 Flash 大多数情况下作为一个浏览器插件(在 Windows 下是一个 ActiveX 控件)的形式存在,但其安全性比之许多绿霸之流的流氓软件还是值得信任的。

身边的同事没有人做过 flash 的应用。周末里,也不好意思的打搅那些做 web game 组的同事。所以我只好求助于 google 以及 adobe 的官方网站,花了几个小时时间,稍微摸出点门道来。

我想我是患上了 IDE 恐惧症。一开始搜出的信息是,开发基于 Flash 的网络应用程序,需要在 Adobe 出品的 Flash CS 中工作。我在网上找来几张这个 IDE 的截图就被吓坏了。这是给我这样愚笨的程序员用的吗?离开了命令行、管道这些东西,我写的代码还能被编译运行吗?我不想把生命浪费在拖拽控件、寻找菜单等等的鼠标操作上。

继续搜索,找到两个东西:

一个是 MTASC ,第三方开源项目,可以用来编译 ActionScript 2 的代码。

另一个是 Adobe 自己 Flex SDK ,可以用来编译 ActionScript 3 。

比较了一下 as 2 和 as3 的语法,很明显,as3 更像是设计出来的语言,虽然写起来比 as2 冗长许多。不过我想,as3 更有前途。所以选择了 Flex SDK 里带的 mxmlc 做我的编译器。

btw, 在 Ubuntu 下安装 MTASC 更为方便,用 apt 即可。随手装了一个,感觉编译速度快很多,有空的话,我也多玩一下。


我想试一下,如果用 as3 创建一个长连接跟 server 通讯。搜索的结果是,使用 flash.net.Socket 或是 flash.net.XMLSocket 。

两个差不太多,不过 Socket 可以用 raw binary 的协议,和我以前做的东西跟贴切。所以我用 flash.net.Socket 做的实验。

Socket 类采用的是回调机制,注册一些事件回调去相应网络消息即可。在 Adobe 的官方文档上找到了例子程序,编译通过。(注:如果使用 mxmlc 编译,需要加上编译开关 -use-network)

然后,我用 nc 架了个简单的 server 做测试。

第一次握手,可耻的失败鸟。nc 收到了一个字符串,“policy-file-request” 然后就被 Client 关掉。

从字面上猜想,这个是 Flash 的安全设施之一,保障 flash 程序挂在 web 上,可以让人更安全的使用。比如,你不能做个 web 页面,放个 flash 在上面,骗别人去浏览,然后用这个 flash 程序去访问一些本来不该你可以访问的服务。

举个例子:如果是你发布一个客户端程序,让用户运行,你可以用这个客户端收集用户机器上的资料,并发回 server ,用户是很难知道的(尤其在 Windows 下)。这点,Flash 可以用本地文件访问的限制控制。

如果你的客户端程序去连接用户本地网络(原本是用防火墙阻止外部连接),用户也很可能在不知情的情况下,泄露一些不应该泄露的信息。Flash 作为一个公众安装量极大的 web 控件,必须考虑这类安全问题。

Flash 的安全解决方案就是,在建立任何网络连接时,先询问 server 自己是否有权限建立连接。也就是自我约束。

为了让我的程序可以正常工作,我在 Adobe 的网站上找到了详细说明

接下来,随手写了几十行 C 程序提供这个 policy-file-request 服务。我的第一个 as3 程序就可以正确的和 server 通讯了。

剩下来的工作,和别的语言/开发环境下做互联网应用基本没有区别:接收包,分析包、处理包。

Flash 在处理 UI 方面还是很强大的,所以搭建一个诸如聊天室,或者是让一个 sprit 在虚拟场景里跑,并同步各个 client 的位置信息都是很容易的事情。

用 Flash 做所谓“无端网游" 大抵如此。只不过把用 C/C++ 写的东西搬到 as3 里做罢了。至于 Server 那边,该怎么做还是怎么做。

June 26, 2009

让 Make 递归所有子目录

最近同事提了一个需求,想方便的调用一个工具校验所有的 xml 文件(包括子目录下的)。我想了一下,最简单的方法是用 Make 来辅助完成这件事情。

问题在于,怎样让 Make 递归的处理所有子目录。因为 GNU Make 默认的 wildcard 只能枚举出当前目录的文件,而不能递归下去。

求助于 Shell 指令当然是一种方法,如果只考虑 unix 环境,我会用 shell 去做。在 Windows 下也有 for 语句可以使用。但我不想处理复杂的平台差异。所以就在 Make 内置的函数里想办法了。

一开始我认为 GNU Make 是个图灵完备的语言,有 call 和 eval 做函数模板和调用。if 函数可以用于递归终止。理论上能写递归就能解决别的语言解决的问题了。

后来试着在 makefile 里写了个简单的递归程序,结果让我很失望。Make (至少是 mingw32 的 make) 被我的小程序整挂了。输出了错误信息:

make: Interrupt/Exception caught (code = 0xc00000fd, addr = 0x7c92e8e7)

迫不得已,我使用了开子进程的方式。(这种方式在 Windows 下效率很低)

下面列出今天写的一段小程序,功能是递归遍历当前目录下的所有子目录。

.PHONY : all

all: 
    echo I'm in $(CURRENT_DIR)

CURRENT_DIR ?= .
FILES = $(wildcard $(CURRENT_DIR)/*)

define DIR_temp
.PHONY : $(1)
all: $(1)
$(1) :
    $$(MAKE) CURRENT_DIR=$$(CURRENT_DIR)/$(strip $(1))
endef

$(foreach e, $(FILES), \
  $(if $(wildcard $(e)/*), \
    $(eval $(call DIR_temp, $(notdir $(e))))))

7 月 8 日补充:

前几天的递归程序是我写错了导致,如果正确的编写是可以成功遍历子目录下的所有文件的。代码如下:

.PHONY : all

# arg1 dir
define EXPAND_temp
  FILES := $(wildcard $(1)*)
  DIRS := 
  $$(foreach e, $$(FILES), $$(if $$(wildcard $$(e)/*), $$(eval DIRS := $$(DIRS) $$(e))))
  FILES := $$(filter-out $$(DIRS),$$(FILES))
  ALLFILES := $$(ALLFILES) $$(FILES)
  $$(foreach e,$$(DIRS),$$(eval $$(call EXPAND_temp,$$(e)/)))
endef

$(eval $(call EXPAND_temp))

all :
    echo $(ALLFILES)

June 22, 2009

近日小结

项目进入一个比较繁忙的阶段。广州先后过来 5 个很有经验的同事一起做开发,各个方面都有条不紊的推进。让我轻松了许多。至少,我有更多的时间,专心写程序了。

目前,自我调整为一周六天工作,每天提早到上午十点半上班,并坚持在晚上两点前回家。维持了一个月后,感觉生物钟有所调整。周末也可以真正的谈的上休息了。

最近两个月居然去了三次电影院,如果不出意外,这周还要去一次看变形金刚。若放在半年前,我是无论如何没有这个兴致的。

新来的同事居然对抱石有兴趣,经过简单指导,已经有明显进步了。我的手指伤也好的差不多了。希望下个月的比赛可以去观摩一下。

另外,我们又多了几个桌游爱好者。前几天开了一局冰与火之歌,加了第二扩展,感觉很有意思。这两天订了一套 small world ,想必这种入门级的游戏可以更多吸引新人。可恶的是,RFTG 的第二扩展继续跳票。

前段时间花了三个晚上研究了下魔方,终于弄明白了。不过速度很慢,平均还原时间要两分多钟。不过这是个练习记忆力和手指灵活度的好玩具,目前随身带着一个。

经过一年的挣扎,和反复研究,我终于买了个新鼠标。罗技的 G3 。今天到货,感觉不错。把原来用的那个 IE 4 扔掉了。(滚轮已坏,右键不灵,真佩服我自己还坚持用了几个月)

我们这里用 Linux 为主要( 90% 时间)桌面的同事又有所增加。大家对 popo 没有非 Windows 版极其愤慨。尤其是连 Wine 都模拟不起来。终于,某人受不了了,开发了Pidgin 插件。整个开发历程真是段血泪史啊。

有多惨?想想一个完全没有通讯协议文档,而包格式无比繁杂的系统。全靠用 popo 跟开发人员聊天,一点点的把通讯协议整理出来。开发过程中,还弄挂了 popo 内部服务器若干次。好吧,我们现在内部公认 popo 的 client 写的很牛,不断更新也没闹出大故障。

话说两三年前,我强烈建议直接使用 XMPP 做新版的 popo 协议未果。悲剧啊。

目前插件支持功能十分有限,所以暂时不能开源和放出。不过,放出有意义么?有多少非公司的人在用 popo 和 Linux ?


前几天一时冲动想买个小本玩玩,看中 Sony 的 P29H 。吃完晚饭冲到公司对面的 Sony 专卖店一问,只有 P17H 这款,冲动消费未果。回来后,看到有帖子说,用多了本颈椎容易出问题;结果第 2 天隔壁的同事去医院检查,还真检查出了颈椎病。

我还是坚持用台式机吧。

另外要多多出去野攀。不爬可以,帮人做保护,可以预防颈椎病。捉保护不是总得仰着头么? :D

June 05, 2009

tcc 的一个 bug

tcc 是个好东西,我们的粒子系统把它当作可选模块,用于动态生成粒子控制代码。较之 lua 的版本,性能可以提高一个数量级(另外一个 gcc 版本的可选模块,会失去动态性)。为了引入这个库,我还好好研究了一下 LGPL 。

前段时间我还在抱怨 tcc 不能跑在 64 bit 环境下,结果今天因为查 bug ,去关心了一下新版本,发现 0.9.25 已经开始支持 64bit 了。

表扬到此为止,现在开始抱怨。

最近两天,我们有两个人陷入了一个诡异的 bug 中,把手头许多工作都停了下来。由于问题过于诡异,引发了许多猜想都一直未能确认。bug 似乎不是特定某段代码引起的,又牵扯进一大段复杂的 3d 渲染模块,稍稍改变一点上下文状态,就会在另一个不相关的代码中出问题。

所以我常说,单元测试或是面向测试的开发绝不是万能药。甚至有时候就是鬼扯。前段看到公司某项目写的大坨的所谓单元测试代码,我就觉得好笑。纯粹是浪费人力。用 1000 行测试 100 行的模块,不如肉眼去看。真正出了问题还找不出来。能起的作用无非也就是找出点拼写错误而已。

有那个时间写这么多测试代码,还不如多写点实际程序,提高点水平,减少 bug 产出。

闲话到此。说说最后定位出来的 bug 。

tcc 动态编译出来的函数,如果使用了浮点寄存器,有时候会在浮点栈上留下一个数字。即占用了 st(0) 。

而 gcc/msvc 通常在编译代码时,认为每次函数调用前后,浮点栈都是干净的,即有 8 个栈空间可用。

所以,在某些边界情况下,编译器生成的代码会企图用满 8 个浮点寄存器。如果中途调用了 tcc 生成的函数,会造成浮点栈溢出。浮点运算的结果不正常。

结论如此简单,但是两三个人耗在里面的时间真是让人心痛阿。我们这几天做的诸多猜测,和各种实验就不详细写了。


末了,我研究了一晚上 tcc 的 source ,打算打个补丁,不过太晚了,暂时还没有头绪。本来想先写封 email 报告一下 bug 。打开了源码包里的 TODO 文件。感觉脑袋后面都是竖线,最前面赫然写着:

Bugs:

  • FPU st(0) is left unclean (kwisatz haderach). Incompatible with optimized gcc/msc code

在 mailling list 上的相关讨论帖

教训:用开源代码前,一定要熟读 bug list 。

June 01, 2009

《链接、装载与库》书评

今年二月份拿到这本书的电子稿时,还不是现在这个名字。

《程序员的自我修养》这个名字听起来比原来的那个名字感觉好一些,但又让人感觉有点不知所谓。还是副标题直接:《链接、装载与库》。我更愿意接受这样的一个名字,有如那本多年前读过的英文经典:《Linkers & Loaders》。

那段时间很忙,一直到现在都是。书稿我压了很久,直到有一天,博文的朋友说,约个时间和 Fenng 、俞甲子等杭州的程序员碰头聚一下。我连夜开始读书稿。不然,见面了谈起这本书来,说不出所以然多不好意思。

书很厚,但是我看的很快。可能是因为内容很熟悉吧。大约花了一个通宵就基本读完了。写的很细,当时快速翻过去的时候,发现几处小笔误,由于我的 pdf 阅读器不能做评注,就忘记了。

我自己写过书,知道写书的艰辛。

很简单的道理,自己本以为明白的很,要写出来让人明白,是件非常不容易的事。前几年,我在 blog 上随手写的一个关于链接方面的帖子,就引起了无穷无尽的争议。Blog 尚可随意为之,可出版成白纸黑字,必然战战兢兢。一旦写错点什么,很容易误导许多人。见过俞甲子以后,我相信他和我一样,是个从内心对读者负责的人。

关于链接,装载等问题。是操作系统中很基础很重要的一个部分。从特定的操作系统的解决方案:分析 Windows 的 PE 或是 Linux 的 ELF 的结构,可以理解操作系统是如何让一段代码工作起来、怎样让不同的二进制模块协同工作的。这是理解整个系统的一个起点。

一方面,我们理解系统如何去做,是为了悟到为何这样去设计。了解了为什么,反过来更能理解怎样去做。我为我的系统做过一段时间的 loader ,为了隔离操作系统的差异,来解决二进制的代码复用。不少朋友问我怎样做到的。其实答案很简单:自己来写 loader 。理解操作系统怎样干的后,这并不是难事。至于需不需要越俎代庖替代操作系统去做,那就需要对这方面更多的理解了。这绝不是简单的是否应该重新发明轮子的问题。

好吧,说起重造轮子的话题,怕又引起无穷尽的争议。但有一点应该是有比较少的争议的。你可以不自己造轮子,但应该了解轮子的构造,而且越详尽越好。这是程序员的自我修养吧。

另一方面,我们对自己每天用的系统多一些了解,那是百利而无一害。即使从最实用主义的角度看,碰到一些表面上看起来无可适从的 bug ,也能分析清楚。比如,我前段时间碰到的那个诡异的 bug


前几天收到博文寄过来的书,本答应周老师写一篇书评,一直没能动笔。端午节休假,终于把这笔欠帐还上了。:) 这本书是本好书,我会放在书架上,相信总有需要的时候拿出来查阅。