很多技术其实都是有技巧的

10条评论 2013-11-01 admin

        很久以前,我以为写程序是不存在技巧的,任何需要的资料都可以在网络上查找到,但最近几年发现这个观点并非百分之百正确。任何一个技术,即使是小到一个按钮的绘制,里面都是有技巧的,而且大多数真正做过的人对这些都三缄其口,留一手。
        老外,慷慨吧,到处都是他们的开源项目,但一些很细节很内幕的东西,他们是秘而不宣的。举个小例子:我经常在Delphi里面使用C语言的东西,因为C语言的资源非常丰富。一般情况下我是翻译,当然,这个是比较繁琐的,因为你要把一门语言的代码翻译到另外一门,那么必需对两门语言都非常精通,所以你不要觉得那个把整个Dirctx库翻译成DSPACK的家伙不熟悉C,他肯定比一般用C的还熟悉,而且翻译完肯定比用C玩Directx的人更加了解这个东西,因为C的只是调用一下,人家可是翻译,不了解透彻,随便一个笔误就OVER。扯远了,回到正题。对于没有多少学习价值而又要使用的C代码,我一般是直接编译成Obj然后直接链接到Delphi里面。
        Delphi XE2发布后,我发现它也支持64位编译了,于是把以前一个代码移植到64位,结果发现里面用到的一个压缩算法是32位的Obj,但C++Builder却没有64位的,后来我发现,Delphi XE2可以直接链接VS 2010编译的64位的OBJ。因为这个OBJ是一个老外发布的免费项目,于是我写信问他是否升级为64位了。他回信说:

How do you build the *.obj files? Please send me the script of your build process so I can take a look. I am right now trying to port all my components to Win64 so your input is much appreciated!

        信件大意是“你是如何生成OBJ的?请发送详细过程给我看看,因为我想把所有控件都升级成64位的”,于是我把详细编译和链接过程给了他。又过了一段时间,我准备链接sqlite的64位,发现编译出obj后,有几个链接错误,最后改了一下,还剩下一个,总是无法解决,我搜索,发现他在卖这个,而且他提供的DMEO里面,链接他编译出来的OBJ是没有问题,于是我写信问他编译OBJ的时候是否有什么要注意的(几个月后我才无意中找到答案,的确是需要加一个很微妙的参数),他没有回信。后来我只有自己掏钱买了一个他的disqlite,结果发现,里面只有32位的编译批处理,64位的只有编译好的OBJ。于是我以正版代码用户的身份再次写信问他是怎么编译出来的,开始他没回信。后来我把我们之前的信件一起发给他,他终于回信了,说:“我这个OBJ肯定是没问题的,你放心用吧,如果发现问题请告诉我。”
        所以,不要以为老外开源很爽,对于关键的东西,他们是不肯透露半点的。
        关于技术是有技巧的,我再举一个例子吧。去年我做P2P通信,于是先GOOGLE了一下,发现流程很简单,UDP打洞即可。比如说,A要和B通信,叫B先往A的端口发送一个数据包,然后A也往B发送一个,如果网络类型支持,那么P2P就成功了。我花了几分钟写了个DEMO,传输个屏幕,好像真的可以。我看到QQ上CCPROXY的作者老朱在线,于是发了一个和他测试,结果发现一个奇怪的问题,就是他那边先发起请求,意思是他的程序控制这边的程序,那么一切都很理想,但反过来就是不行,我排除了网络类型的缘故,还是如此。于是我跟他说了这个现象,他也觉得这么简单的打洞不可能有什么问题的,于是把他以前弄的P2P发给我,测试后发现他的程序也是如此,于是我们都陷入了沉思。疯狂GOOGLE,结果到处都是P2P大师们写的打洞教程,很多还附上了测试代码,流程都是一样的,但就是没有人提及这个问题,跟帖的留言也没有人提及这个,难道他们都没遇到过这个现象?后来终于在CSDN的论坛发现有一位难兄难弟也问过这个,但最终也没人能回答。最后只有自己上,SNIFF,抓包,才发现原因:原来我的路由器,在收到对方的数据包后,发现没有这个IP映射,就把这个端口暂时给关闭了,15分钟内发给这个端口的包都丢弃(可能它以为收到了UDP攻击包?),同时给对方回复了一个表示目标不可到达的ICMP包。于是我只有打两次洞了,同时用不同的端口,一个是我先打,一个是对方先打,哪个成功用哪个。后来我抓包,发现QQ其实也是这么干的,传输文件,也是双方用不同端口,不同顺序一起打的。
        最后再举一个例子,完成端口。搜索一下完成端口,也是到处都可以发现完成端口大师们的文章,而且基本流程都是这样的:第一句:“完成端口(IOCP)是迄今为止最为高效的网络模型。”接着噼哩叭啦的一堆API声明调用,最后还TMD不忘来个DEMO:一个回显程序,接受连接,收到一个数据包就回复一个。就这样,无数无知少男被骗。其实,这种DEMO跟实用还差的远。在这种教程下写出来的完成端口代码,我也看过不少,包括一个很出名的网络游戏公司的代码,这些人根本就不了解什么叫完成端口。举几个简单例子:比如说,对方开了8个完成线程来收发数据,结果呢?每个SOCKET都是只投递一个收发,这叫高效的完成端口么?当然,可能他也知道,同时投递多个的话,可能因为8个线程进出顺序导致乱包,必须做序列处理,嫌麻烦。好,再来看收到数据后,把数据给处理线程处理,结果他LOCK了,哥,完成端口你LOCK什么。。。8个完成线程,每收到一个包,就LOCK,把包塞到处理线程的队列后再UNLOCK,这完全是阻塞模式的思想,你即使无法从逻辑上做到无锁,至少上个LOCKFREE也好啊。再来看心跳检测:建立一个LIST,轮询,每收到一个数据包就更新一次最后通信时间。哥,如果有3万连接,你不是无时无刻的3万次循环?简单点的你也上个轮盘算法啊。至于其它一些小细节就更加不用说了,比如说,8个完成线程,3个线程收发包达到90%,另外几个永远就是几KB的数据。
        最后要发的牢骚是,遇到问题,自己去找答案永远比网络靠的住,千万不要偷懒。前几天做一个声音编码解码,AAC算法,发现也是到处都是这种文章。花了几分钟,直接先调用C的DLL。因为我做的是一个直播系统,发送端AAC压缩,播放端FAAD解压,发现大多少视频文件是正常的,但有几个声音播放起来明显延迟。一开始,我怀疑是时间戳没打对,检查后,发现不是这里的原因。最后定位到是FAAD解压造成的:不管压缩前是什么频率的,解压后都统一变成44100了。而有问题那几个文件,一些是33200的,一些是11025的,没问题的都是44100的。GOOGLE,也是无果,到处都是AAC和FAAD大师们的官方的翻译和例子---而官方的DEMO也是有这个问题的,文档是好几年前的了,有些参数跟现在的版本接口都不一样了。CSDN论坛倒是是有一个人问过,一样没有答案。找了个权威的流媒体公司的技术人员问,他说只要经过那个初始化函数后,都会这样的,后来他们就限定输入的只能是44100。专业人士都这么说了,于是我放弃了,曲线救国吧,收到数据后,再根据原来的格式强制转换一次。过了几天,暂时忙完公司的事情,我着手把那个C代码转换成OBJ的,然后无意跟了一下代码,才发现是初始化的时候,它强制转换的:

long NEAACDECAPI NeAACDecInit(NeAACDecHandle hpDecoder,
                              unsigned char *buffer,
                              unsigned long buffer_size,
                              unsigned long *samplerate,
                              unsigned char *channels)

......
#ifdef SBR_DEC
    /* implicit signalling */	
    if (*samplerate <= 24000 && (hDecoder->config.dontUpSampleImplicitSBR == 0))
    {
        *samplerate *= 2;
        hDecoder->forceUpSampling = 1;
    } else if (*samplerate > 24000 && (hDecoder->config.dontUpSampleImplicitSBR == 0)) {
        hDecoder->downSampledSBR = 1;
    }	
#endif
......

        最后解决就简单了,初始化前把参数dontUpSampleImplicitSBR设置为1即可。这个案例说明,很多所谓的权威,专业人士,有时候也是很懒惰的。很多时候,他们也是直接调用而已,并没有真正深入,类似这个问题,看一眼官方的代码就可以知道原因了。至于官方为什么没有详细说明,可能他认为你会看代码?毕竟开源的东西,文档之类你不能要求那么高的。

分类:未分类

10条评论 发表评论

  • eliachen说道:

    nice啊!

  • delphier说道:

  • 谷月轩说道:

    解决了我网上找不到答案的困惑!

  • GR44说道:

    经韬大哥就是牛逼啊 ,这种精神值得我们delphi fans好好学习

  • 饭桶超人说道:

    感谢经韬兄的教诲!!

  • rushpixy说道:

    很多东西还没进化到那个阶段。
    所以 细节太多了,而网上的都粘来粘去,没去解决实际问题。

  • 自开说道:

    同感,很多东西都得靠自己去解决,网络和别人都只是辅助。

  • steven说道:

    好,难得高人分享

  • zwjchina说道:

    有疑惑想请教:
    “我也看过不少,包括一个很出名的网络游戏公司的代码,这些人根本就不了解什么叫完成端口。举几个简单例子:比如说,对方开了8个完成线程来收发数据,结果呢?每个SOCKET都是只投递一个收发,这叫高效的完成端口么?”

    游戏服务器,同时为成千上万个socket服务,并不一定要追求在一个单一的socket上的高吞吐量。个人认为,每个socket只投递一个收发,似乎并无不妥。

  • 一克糯米斯特说道:

    解决了长久以来心中的困惑,本文的道理放在生活上也一样有意义:一切困难的解决,最终还需要依靠自己。

发表评论

(必填)

(必填), (Hidden)

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

TrackBack URL  |  这篇文章上的评论的RSS feed


近期文章

近期评论

文章归档

分类目录