關於微信內部正在使用的網絡層封裝庫Mars開源的消息,1個多月前就已滿天飛(參見《微信Mars:微信內部正在使用的網絡層封裝庫,即將開源》),不過微信團隊沒有失約,微信Mars 於2016年12月28日正式公開源碼(源碼地址:https://github.com/Tencent/mars,也可從本文文末的附件下載之,Android版演示程序能夠從文末的附件中下載)。
以前不管是微信團隊仍是手機QQ團隊,都以騰訊公司的名義在Github開源了數個工程,但這些工程所受的關注度遠不及Mars。之因此Mars廣受關注的緣由,其實搞移動端IM或推送技術的開發者同行都明白,由於移動網絡實在太不可靠、太複雜,以致於寫出一個能用於大規模用戶環境的穩定、省流量、省電、數據傳輸流暢、弱網絡健壯、後臺自動保活等技術指標的IM或推送是至關困難的。
更爲重要的緣由是畢竟微信Mars通過微信團隊多年積累並通過海量用戶的測試和使用,是經受的住各類複雜移動端網絡環境、各類亂七八糟型號智能手機的真實考驗的。若Mars開源,必將爲IM及相關技術應用領域的同行帶來不少有價值的實踐成果,畢竟微信的體量和應用規模決定了技術的高度,確實是值得同行學習和關注。
以前的文章,好比《微信移動端應對弱網絡狀況的探索和實踐PPT》、《微信Mars:微信內部正在使用的網絡層封裝庫,即將開源》,也都或多或少對Mars進行了初步介紹,但微信Mars究竟是個啥玩意,它能解決啥問題?
咱們簡要的歸納一下,微信Mars解決了以下問題:html
以上特色,還不盡于歸納微信Mars的技術特徵,建議對C++熟悉的IM或推送技術同行能夠直接去看看Mars源碼。
那麼微信Mars到底有什麼用呢?毫無疑問,微信Mars存在的前提就是爲了更好的服務微信這個超級IM而存在,最適合乾的活就是開發移動端IM了,固然因爲提煉的很好,相信移動端推送技術等都是可使用微信Mars做爲網絡層lib來使用,從而以微信的成果爲起點開發出擁有更加優秀網絡體驗的移動端富網絡應用。
好了,言歸正傳,本文正文內容引用了微信開發團隊的資料,請繼續往下閱讀。(本文同步發佈於:http://www.52im.net/thread-684-1-1.html)前端
《移動端IM實踐:實現Android版微信的智能心跳機制》
《微信Mars:微信內部正在使用的網絡層封裝庫,即將開源》
《微信移動端應對弱網絡狀況的探索和實踐PPT [附件下載]》
《微信異步化改造實踐:8億月活、單機千萬鏈接背後的後臺解決方案》android
2012 年中,微信支持包括 Android、iOS、Symbian 三個平臺。但在各個平臺上,微信客戶端沒有任何統一的基礎模塊。2012 年的微信正處於高速發展時期,各平臺的迭代速度不1、使用的編程語言各異,後臺架構也處在不斷探索的過程當中。多種因素使得各個平臺基礎模塊的實現出現了差別,致使出現屢次須要服務器作兼容的善後工做。網絡做爲微信的基礎,重要性不言而喻。任何網絡實現的 bug 均可能致使重大事故。例如微信的容災實現,若是由於版本的實現差別,致使某些版本上沒法進行容災恢復,將會嚴重的影響用戶體驗,甚至形成用戶的流失。咱們亟需一套統一的網絡基礎庫,爲微信的高速發展保駕護航。
剛好,這個時候塞班漸入日暮,微信對塞班的支持也逐漸減弱。老大從塞班組抽調人力,組成一個三人小 team 的初始團隊,開始着手作通用的基礎組件。這個基礎組件最初就定位爲:跨平臺、跨業務的基礎組件。如今看,這個組件除了解決了已有問題,還給微信的高速發展帶來了不少優點,例如:git
通過四年多的發展,跨平臺的基礎組件已經包含了網絡組件、日誌組件在內的多個組件。回頭看,這是一條開荒路。github
在基礎模塊的開發中,設計尤其重要。在設計上,微信基礎組件以跨平臺、跨業務爲前提,聽從高可用,高性能,負載均衡的設計原則。
可用是一個即時通信類 App 的立身之本。高可用又體如今多個層面上:網絡的可用性、 App 的可用性、系統的可用性等。web
保障高可用並不表明能夠犧牲性能,對於一個用戶使用最頻繁的應用,反而更要對使用的資源精打細算。例如在 Mars 信令傳輸超時設計中,多級超時的設計充分的考慮了可用性與高性能之間的平衡取捨。
若是說高可用高性能只是客戶端自己的考慮的話,負載均衡就須要結合服務器端來考慮了,作一個客戶端網絡永遠不能只把眼光放在客戶端上。任何有關網絡訪問的決策都要考慮給服務器所帶來的額外壓力是多大。爲了選用質量較好的 IP,曾經寫了完整的客戶端測速代碼,後來刪掉,其中一個緣由是由於不想給服務器帶來額外的負擔。Mars 的代碼中,選擇 IP 時用了大量的隨機函數也是爲了規避大量的用戶同時訪問同一臺服務器而作的。
在這四年,我學到最多的就是簡單和平衡。 把方案作的儘量簡單,這樣纔不容易出錯。設計方案時大多數時候都不可能知足全部想達到的條件,這個時候就須要去平衡各個因素。在組件中一個很好的例子就是長鏈接的鏈接頻率(具體實現見longlink_connect_monitor.cc),這個鏈接頻率就是綜合耗電量,流量,網絡高可用,用戶行爲等因素進行綜合考慮的。算法
跨平臺基礎組件的需求起源於微信,首要目標固然是先承載起微信業務。爲了避免侷限於微信,知足跨平臺、跨業務的設計目標,在設計上,網絡組件定位爲客戶端與服務端之間的無狀態網絡信令通道,即交互方式主要包含一來一回、主動push兩種方式。這使得基礎組件無需考慮請求間的關聯性、時序性,核心接口獲得了極大的簡化。同時,簡潔的交互也使得業務邏輯的耦合極少。目前基礎組件與業務的交互只包括:編解碼、auth狀態查詢兩部分(具體見stn_logic.h)。編程
void StartTask(...); int OnTaskEnd(...); void OnPush(...); bool Req2Buf(...); int Buf2Resp(...); bool MakeSureAuthed();
在線程模型的選擇上,最先使用的是多線程模型。當須要異步作一個工做,就起一個線程。多線程勢必少不了鎖。但當灰度幾回以後發現,想要規避死鎖的四個必要條件並無想象中的那麼容易。用戶使用場景複雜,客戶端的時序、狀態的影響因素多,例如網絡切換事件、先後臺事件、定時器事件、網絡事件、任務事件等,致使了很多的死鎖現象和對象析構時序錯亂致使的內存非法訪問問題。
這時,咱們開始思考,多線程確實有它的優勢:能夠併發甚至並行提升運行速度。可是對於網絡模塊來講,性能瓶頸主要是在網絡耗時上,並不在於本地程序執行速度上。那爲什麼不把大部分程序執行改爲串行的,這樣就不會存在多線程臨界區的問題,無鎖天然就不會死鎖。
所以,咱們目前使用了消息隊列的方案(具體實現見 comm/messagequeue 目錄),把絕大多數非阻塞操做放到消息隊列裏執行。而且規定,基礎組件與調用方之間的交互必須1. 儘快完成,不進行任何阻塞操做;2. 單向調用,避免造成環狀的複雜時序。消息隊列的引入很好的改善了死鎖問題,但消息隊列的線程模型中,咱們仍是不能避免存在須要阻塞的調用,例如網絡操做。在將來的嘗試中,咱們計劃引入協程的方式,將線程模型儘量的簡化。
在其它技術選型上,有時甚至須要細節到API 的使用,好比考慮平臺兼容性問題,捨棄了一些函數的線程安全版本,使用了 asctime、localtime、rand 等非線程安全的版本。安全
在屢次的灰度驗證、數據比對下,微信各平臺的網絡邏輯順利的過渡到了統一基礎組件。爲了有效的驗證組件的效果,咱們開發了 smc 的統計監控組件,開始關注網絡的各項指標,進行網絡基礎研究與優化,尤爲是關注移動網絡的特徵。
基礎組件全量上線微信後,以微信的用戶量,固然也會遇到各類各樣的「妖」。例如,寫網絡程序躲不開運營商。印象比較深入的某地的用戶反饋鏈接 WiFi 時,微信不可用,後來 tcpdump 發現,當包的大小超過必定大小後就發不出去。解決方案:在 WiFi 網絡下強制把 MSS 改成1400(代碼見 unix_socket.cc)。
作移動客戶端更避不開手機廠商。一次遇到了一個百思不得其解的 crash,堆棧以下:
#00 pc 0x43e50 /system/lib/libc.so (???)
#01 pc 0x3143 /system/vendor/lib/libvendorconn.so (handleDpmIpcReq+154) #02 pc 0x2f6d /system/vendor/lib/libvendorconn.so (send_ipc_req+276) #03 pc 0x30ff /system/vendor/lib/libcneconn.so (connect+438)
看堆棧結合程序 xlog 分析,非阻塞 socket 卡在了 connect 函數裏超過了6 min, 被咱們自帶的 anr 檢測(代碼見anr.cc)發現而後自殺。最後實在一籌莫展,聯繫廠商一塊兒排查,最終查明緣由:爲了省電,當手機鎖屏時連的不是 WiFi 且又沒有下行網絡數據時,芯片 gate 會關閉,block 住全部網絡請求,直到有下行數據或者超過 20min 纔會放開。當手機有網絡即便是手機網絡的狀況下,很難沒有下行數據,因此基本不會觸發組件自帶的 anr 檢測,但當手機沒鏈接任何網絡時,就很容易觸發。解決方案:廠商修改代碼邏輯,當沒有任何網絡時不 block 網絡請求。
運營商和手機廠商對咱們來講已是一個黑盒,但其實也遇到過更黑的黑盒。當手機長時間不重啓,有極小几率不能繼續使用微信,重啓手機會恢復。但由於一直找不到一個願意配合咱們又知足條件的用戶,致使這個問題很長一段時間內都沒有任何進展,最終偶然一個機會,在一臺測試機器上重現了該問題,tcpdump 發如今三步握手階段,服務器帶回的客戶端帶過去的 tsval 字段被篡改,致使三步握手直接失敗,並且這個篡改發生在離開服務器以後到達客戶端以前。
這個問題是微信網絡模塊中排查時間最長也是花費精力最多的一個問題,不只由於很長一段時間內無案例可分析,也由於在重現後,聯繫了大量的同事和外部有關人的幫忙,想排查出罪魁禍首。但由於中間涉及的環節和運營商相關部門過多,沒法繼續排查下去,最終也沒找到根本緣由。 解決辦法:服務器更改 net.ipv4.tcp_timestamps = 0。
這段時間是痛並快樂着,見識到了各類極差的網絡,才切膚感覺到移動網絡環境的惡劣程度,但看着咱們的網絡性能數據在穩步提高又有種知足感。截止到今天,已經不多有真正的網絡問題須要跟進了。這也是咱們能有時間開始把這些代碼開源出去的很大的一個緣由。
大概一年前(大約2015年10月),咱們開始有想法把基礎組件開源出去,當時你們都在糾結叫什麼名字好呢?此時恰逢《火星救援》正在熱映,一位同事說乾脆叫 Mars 吧,因而就定下來叫了 Mars。看了看代碼,發現想要開源出去可能仍是須要作一些其餘工做的。
首先,代碼風格方面,由於最初咱們使用文件名、函數名、變量名的規則是內部定義的規則,爲了能讓其餘人讀起來更舒心,咱們決定把代碼風格改成谷歌風格,好比:變量名一概小寫, 單詞之間用下劃線鏈接;左大括號不換行等等。可是爲了更好的區分訪問空間,咱們又在谷歌代碼風格進行了一些變通,好比:私有函數所有是」__」開頭;函數參數所有以」_」開頭 等等。
其次,雖然最初的設計一直是秉承着業務性無關的設計,但在實際開發過程當中仍然不免帶上了微信的業務性相關代碼,比較典型的就是 newdns 。爲了 Mars 之後的維護以及保證開源出去代碼的同源,在開源出去以前必須把這些業務性有關的代碼抽離出來,抽離後的結構以下:
最後,爲了接口更易用,對調用接口以及回調接口的參數也進行了反覆思考與修改。
在 Mars以前,是直接給 Android 提供動態庫(.so),由於代碼邏輯都已經固定,不須要有可定製的部分。給 Apple 系平臺提供靜態庫(.a),由於對外暴露的函數幾乎不會改變,直接把相應的頭文件放到相應的項目裏就行。但對外開源就徹底不同了:日誌的加密算法可能別人須要本身實現;長連或者短連的包頭有人須要本身定製;對外接口的頭文件咱們可能會修改……
爲了讓使用者可定製代碼,對於編譯 Android 平臺咱們提供了兩種選擇:
編譯出來靜態庫後,實現本身須要定製的代碼後,執行 ndk-build 後便可編譯出來動態庫。 對於 Apple 系平臺,把頭文件所有收攏爲 Mars 維護,直接編譯出 Framework。
爲了能讓開發者快速的入門,咱們提供了 Android、iOS、OS X 平臺的 demo(微信開源Mars的Demo源碼點此進入),其餘平臺的編譯和 demo 會在不久就加上支持。
▲ 成型的 Mars 結構圖如上圖所示
咱們作的一直都不是知足全部需求的組件,只是作了一個更適合咱們使用的組件,這裏也列了下和同類型的開源代碼的對比。
能夠看出:
總的來講,Mars 是一個結合移動 App 所設計的基於 socket 層的解決方案,在網絡調優方面有更好的可控性,對於 HTTP 完整協議的支持,已經考慮後續版本會加入。
▲ 截止2016年12月28日微信Mars的開源工程代碼結構
試用 Android Demo 請從文末的附件下載之,iOS sample 請經過 Mars的Github倉庫 編譯得到(或文末的附件下載源碼)。
常常有朋友和我說:發現網絡信號差的時候或者其餘應用不能用的時候,微信仍然能發出去消息。不知不覺咱們好像什麼都沒作,回頭看,原來咱們已經作了這麼多。
我想,並非任何一行代碼均可以經歷日活躍5億用戶的考驗,感謝微信給咱們提供了這麼一個平臺。如今咱們想把這些代碼和大家分享,運營方式上 Mars 所開源出去的代碼會和微信所用的代碼保持同源,全部開源出去的代碼也首先會在微信上驗證經過後再公開。
開源並非結束,只是開始。咱們後續仍然會繼續探索在移動互聯網下的網絡優化。Talk is cheap,show you our code。
(本文同步發佈於:http://www.52im.net/thread-684-1-1.html,本文引用了微信團隊原創文章:原始文字點此進入)
[1] 網絡編程基礎資料:
《TCP/IP詳解 - 第11章·UDP:用戶數據報協議》
《TCP/IP詳解 - 第17章·TCP:傳輸控制協議》
《TCP/IP詳解 - 第18章·TCP鏈接的創建與終止》
《TCP/IP詳解 - 第21章·TCP的超時與重傳》
《技術往事:改變世界的TCP/IP協議(珍貴多圖、手機慎點)》
《通俗易懂-深刻理解TCP協議(上):理論基礎》
《通俗易懂-深刻理解TCP協議(下):RTT、滑動窗口、擁塞處理》
《理論經典:TCP協議的3次握手與4次揮手過程詳解》
《理論聯繫實際:Wireshark抓包分析TCP 3次握手、4次揮手過程》
《計算機網絡通信協議關係圖(中文珍藏版)》
《UDP中一個包的大小最大能多大?》
《Java新一代網絡編程模型AIO原理及Linux系統AIO介紹》
《NIO框架入門(一):服務端基於Netty4的UDP雙向通訊Demo演示》
《NIO框架入門(二):服務端基於MINA2的UDP雙向通訊Demo演示》
《NIO框架入門(三):iOS與MINA二、Netty4的跨平臺UDP雙向通訊實戰》
《NIO框架入門(四):Android與MINA二、Netty4的跨平臺UDP雙向通訊實戰》
《P2P技術詳解(一):NAT詳解——詳細原理、P2P簡介》
《P2P技術詳解(二):P2P中的NAT穿越(打洞)方案詳解》
《P2P技術詳解(三):P2P技術之STUN、TURN、ICE詳解》
《高性能網絡編程(一):單臺服務器併發TCP鏈接數到底能夠有多少》
《高性能網絡編程(二):上一個10年,著名的C10K併發鏈接問題》
《高性能網絡編程(三):下一個10年,是時候考慮C10M併發問題了》
《高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索》
>> 更多同類文章 ……
[2] 有關IM/推送的通訊格式、協議的選擇:
《爲何QQ用的是UDP協議而不是TCP協議?》
《移動端即時通信協議選擇:UDP仍是TCP?》
《如何選擇即時通信應用的數據傳輸格式》
《強列建議將Protobuf做爲你的即時通信應用數據傳輸格式》
《移動端IM開發須要面對的技術問題(含通訊協議選擇)》
《簡述移動端IM開發的那些坑:架構設計、通訊協議和客戶端》
《理論聯繫實際:一套典型的IM通訊協議設計詳解》
《58到家實時消息系統的協議設計等技術實踐分享》
>> 更多同類文章 ……
[3] 有關IM/推送的心跳保活處理:
《Android進程保活詳解:一篇文章解決你的全部疑問》
《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》
《爲什麼基於TCP協議的移動端IM仍然須要心跳保活機制?》
《微信團隊原創分享:Android版微信後臺保活實戰分享(進程保活篇)》
《微信團隊原創分享:Android版微信後臺保活實戰分享(網絡保活篇)》
《移動端IM實踐:實現Android版微信的智能心跳機制》
《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》
>> 更多同類文章 ……
[4] 有關WEB端即時通信開發:
《新手入門貼:史上最全Web端即時通信技術原理詳解》
《Web端即時通信技術盤點:短輪詢、Comet、Websocket、SSE》
《SSE技術詳解:一種全新的HTML5服務器推送事件技術》
《Comet技術詳解:基於HTTP長鏈接的Web端實時通訊技術》
《WebSocket詳解(一):初步認識WebSocket技術》
《socket.io實現消息推送的一點實踐及思路》
《LinkedIn的Web端即時通信實踐:實現單機幾十萬條長鏈接》
>> 更多同類文章 ……
[5] 有關IM架構設計:
《淺談IM系統的架構設計》
《簡述移動端IM開發的那些坑:架構設計、通訊協議和客戶端》
《一套原創分佈式即時通信(IM)系統理論架構方案》
《從零到卓越:京東客服即時通信系統的技術架構演進歷程》
《蘑菇街即時通信/IM服務器開發之架構選擇》
《騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT》
《微信技術總監談架構:微信之道——大道至簡(演講全文)》
《如何解讀《微信技術總監談架構:微信之道——大道至簡》》
《快速裂變:見證微信強大後臺架構從0到1的演進歷程(一)》
《17年的實踐:騰訊海量產品的技術方法論》
>> 更多同類文章 ……
[6] 有關IM安全的文章:
《即時通信安全篇(一):正確地理解和使用Android端加密算法》
《即時通信安全篇(二):探討組合加密算法在IM中的應用》
《即時通信安全篇(三):經常使用加解密算法與通信安全講解》
《即時通信安全篇(四):實例分析Android中密鑰硬編碼的風險》
《即時通信安全篇(五):對稱加密技術在Android平臺上的應用實踐》
《即時通信安全篇(六):非對稱加密技術的原理與應用實踐》
《傳輸層安全協議SSL/TLS的Java平臺實現簡介和Demo演示》
《理論聯繫實際:一套典型的IM通訊協議設計詳解(含安全層設計)》
《微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解》
《來自阿里OpenIM:打造安全可靠即時通信服務的技術實踐分享》
>> 更多同類文章 ……
[7] 有關實時音視頻開發:
《即時通信音視頻開發(一):視頻編解碼之理論概述》
《即時通信音視頻開發(二):視頻編解碼之數字視頻介紹》
《即時通信音視頻開發(三):視頻編解碼之編碼基礎》
《即時通信音視頻開發(四):視頻編解碼之預測技術介紹》
《即時通信音視頻開發(五):認識主流視頻編碼技術H.264》
《即時通信音視頻開發(六):如何開始音頻編解碼技術的學習》
《即時通信音視頻開發(七):音頻基礎及編碼原理入門》
《即時通信音視頻開發(八):常見的實時語音通信編碼標準》
《即時通信音視頻開發(九):實時語音通信的迴音及迴音消除概述》
《即時通信音視頻開發(十):實時語音通信的迴音消除技術詳解》
《即時通信音視頻開發(十一):實時語音通信丟包補償技術詳解》
《即時通信音視頻開發(十二):多人實時音視頻聊天架構探討》
《即時通信音視頻開發(十三):實時視頻編碼H.264的特色與優點》
《即時通信音視頻開發(十四):實時音視頻數據傳輸協議介紹》
《即時通信音視頻開發(十五):聊聊P2P與實時音視頻的應用狀況》
《即時通信音視頻開發(十六):移動端實時音視頻開發的幾個建議》
《即時通信音視頻開發(十七):視頻編碼H.26四、V8的前世此生》
《網易視頻雲技術分享:音頻處理與壓縮技術快速入門》
《學習RFC3550:RTP/RTCP實時傳輸協議基礎知識》
《簡述開源實時音視頻技術WebRTC的優缺點》
《良心分享:WebRTC 零基礎開發者教程(中文)》
《開源實時音視頻技術WebRTC中RTP/RTCP數據傳輸協議的應用》
《基於RTMP數據傳輸協議的實時流媒體技術研究(論文全文)》
《聲網架構師談實時音視頻雲的實現難點(視頻採訪)》
《淺談開發實時視頻直播平臺的技術要點》
《還在靠「喂喂喂」測試實時語音通話質量?本文教你科學的評測方法!》
《實現延遲低於500毫秒的1080P實時音視頻直播的實踐分享》
《移動端實時視頻直播技術實踐:如何作到實時秒開、流暢不卡》
《如何用最簡單的方法測試你的實時音視頻方案》
《技術揭祕:支持百萬級粉絲互動的Facebook實時視頻直播》
>> 更多同類文章 ……
[8] IM開發綜合文章:
《移動端IM開發須要面對的技術問題》
《開發IM是本身設計協議用字節流好仍是字符流好?》
《請問有人知道語音留言聊天的主流實現方式嗎?》
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《談談移動端 IM 開發中登陸請求的優化》
《徹底自已開發的IM該如何設計「失敗重試」機制?》
《微信對網絡影響的技術試驗及分析(論文全文)》
《即時通信系統的原理、技術和應用(技術論文)》
《開源IM工程「蘑菇街TeamTalk」的現狀:一場虎頭蛇尾的開源秀》
《騰訊原創分享(一):如何大幅提高移動網絡下手機QQ的圖片傳輸速度和成功率》
《如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》
>> 更多同類文章 ……
[9] 開源移動端IM技術框架資料:
《開源移動端IM技術框架MobileIMSDK:快速入門》
《開源移動端IM技術框架MobileIMSDK:常見問題解答》
《開源移動端IM技術框架MobileIMSDK:壓力測試報告》
>> 更多同類文章 ……
[10] 有關推送技術的文章:
《iOS的推送服務APNs詳解:設計思路、技術原理及缺陷等》
《Android端消息推送總結:實現原理、心跳保活、遇到的問題等》
《掃盲貼:認識MQTT通訊協議》
《一個基於MQTT通訊協議的完整Android推送Demo》
《IBM技術經理訪談:MQTT協議的制定歷程、發展示狀等》
《求教android消息推送:GCM、XMPP、MQTT三種方案的優劣》
《移動端實時消息推送技術淺析》
《掃盲貼:淺談iOS和Android後臺實時消息推送的原理和區別》
《絕對乾貨:基於Netty實現海量接入的推送服務技術要點》
《移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)》
《爲什麼微信、QQ這樣的IM工具不使用GCM服務推送消息?》
《極光推送系統大規模高併發架構的技術實踐分享》
《從HTTP到MQTT:一個基於位置服務的APP數據通訊實踐概述》
>> 更多同類文章 ……
[11] 更多即時通信技術好文分類:
http://www.52im.net/forum.php?mod=collection&op=all