很多技術其實都是有技巧的(自己去找答案永遠比網絡靠的住,千萬不要偷懶)
很久以前,我以為寫程序是不存在技巧的,任何需要的資料都可以在網絡上查找到,但最近幾年發現這個觀點並非百分之百正確。任何一個技術,即使是小到一個按鈕的繪制,裏面都是有技巧的,而且大多數真正做過的人對這些都三緘其口,留一手。
老外,慷慨吧,到處都是他們的開源項目,但一些很細節很內幕的東西,他們是秘而不宣的。舉個小例子:我經常在Delphi裏面使用C語言的東西,因為C語言的資源非常豐富。一般情況下我是翻譯,當然,這個是比較繁瑣的,因為你要把一門語言的代碼翻譯到另外一門,那麽必需對兩門語言都非常精通,所以你不要覺得那個把整個Dirctx庫翻譯成DSPACK的家夥不熟悉C,他肯定比一般用C的還熟悉,而且翻譯完肯定比用C玩Directx的人更加了解這個東西,因為C的只是調用一下,人家可是翻譯,不了解透徹,隨便一個筆誤就OVER。扯遠了,回到正題。對於沒有多少學習價值而又要使用的C代碼,我一般是直接編譯成Obj然後直接鏈接到Delphi裏面。
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的,然後無意跟了一下代碼,才發現是初始化的時候,它強制轉換的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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即可。這個案例說明,很多所謂的權威,專業人士,有時候也是很懶惰的。很多時候,他們也是直接調用而已,並沒有真正深入,類似這個問題,看一眼官方的代碼就可以知道原因了。至於官方為什麽沒有詳細說明,可能他認為你會看代碼?畢竟開源的東西,文檔之類你不能要求那麽高的。
http://www.138soft.com/?p=233
很多技術其實都是有技巧的(自己去找答案永遠比網絡靠的住,千萬不要偷懶)