<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    1、引言

    特別說明:本文內容僅用于即時通訊技術研究和學習之用,請勿用于非法用途。如本文內容有不妥之處,請聯系JackJiang進行處理!

    我司有關部門為了獲取黑產群的動態,有同事潛伏在大量的黑產群(QQ群、微信群)中,干起了無間道的工作。隨著黑產群數量的激增,同事希望能自動獲取黑產群的聊天信息,并交付風控引擎進行風險評估。于是,這個工作就交給我了,是時候表現一波了……

    針對同事的需求,分析了一通,總結一下:

    1)能夠自動獲取微信和 QQ群的聊天記錄;

    2)只要文字記錄,圖片和表情包,語音之類的不要;

    3)后臺自動運行,非實時獲取記錄。

    注:本文讀取聊天記錄的方法只適用于監控自己擁有的微信或者QQ ,無法監控或者盜取其他人的聊天記錄。本文只寫了如何獲取聊天記錄,服務器落地程序并不復雜,不做贅述。寫的倉促,有錯別字還請見諒。)

    學習交流:

    - 即時通訊開發交流3群:185926912[推薦]

    - 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

    (本文同步發布于:http://www.52im.net/thread-1992-1-1.html

    2、相關文章

    即時通訊網之前整理過微信本地數據庫的讀取和樣本,如有興趣可請往閱讀:

    微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]

    3、準備工作

    參閱很多相關的文章之后,對這個需求有了大致的想法,開始著手準備:

    1)需要一個有root權限的Android手機,我用的是紅米5(強調必須已被ROOT);

    2)android的開發環境(就是Android Studio那一套啦);

    3)android相關的開發經驗(我是個PHP,第一次寫Android程序,踩了不少坑)。

    4、獲取微信聊天記錄過程分享

    4.1 著手準備

    微信的聊天記錄保存在Android系統的:"/data/data/com.tencent.mm/MicroMsg/c5fb89d4729f72c345711cb*/EnMicroMsg.db" 目錄和文件下。

    該文件是加密的數據庫文件,需要用到sqlcipher來打開。密碼為:MD5(手機的IMEI+微信UIN)的前七位。文件所在的那個亂碼文件夾的名稱也是一段加密MD5值:MD5('mm'+微信UIN)。微信的UIN存放在微信文件夾“/data/data/com.tencent.mmshared_prefs/system_config_prefs.xml”中。(這個減號一定要帶著!)

    另外:即時通訊網之前整理過微信本地數據庫的樣本,如有興趣可請往下載:《微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]》。

    注意:如果手機是雙卡雙待,那么會有兩個IMEI號,默認選擇 IMEI1,如果不行,可以嘗試一下字符串‘1234567890ABCDEF’。早期的微信會去判定你的IMEI,如果為空 默認選擇這個字符串。

    拿到密碼,就可以打開EnMicroMsg.db了。微信聊天記錄,包括個人、群組的所有記錄全部存在message這張表里(如下圖所示),就像下面這兩張截圖里展示的一樣。

    (為了方便截圖,此圖截自《微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]》中的樣本)

    (為了方便截圖,此圖截自《微信本地數據庫破解版(含iOS、Android),僅供學習研究 [附件下載]》中的樣本)

    4.2 代碼實現

    第一步,不可能直接去訪問EnMicroMsg.db。因為沒有權限,還要避免和微信本身產生沖突,所以選擇把這個文件拷貝到自己的項目下:

    oldPath ="/data/data/com.tencent.mm/MicroMsg/c5fb89d4729f72c345711cb**\***/EnMicroMsg.db";

    newPath ="/data/data/com.你的項目/EnMicroMsg.db";

    copyFile(oldPath,newPath);//代碼見 部分源碼

    第二步,拿到文件的密碼:

    String password = (MD5Until.md5("IMEI+微信UIN").substring(0, 7).toLowerCase());

    第三步,打開文件,執行SQL:

    SQLiteDatabase.loadLibs(context);

    SQLiteDatabaseHook hook = newSQLiteDatabaseHook() {

        publicvoidpreKey(SQLiteDatabase database) {

        }

        publicvoidpostKey(SQLiteDatabase database) {

            database.rawExecSQL("PRAGMA cipher_migrate;");//很重要

        }

    };

    SQLiteDatabase db = openDatabase(newPath, password, null, NO_LOCALIZED_COLLATORS, hook);

        longnow = System.currentTimeMillis();

        Log.e("readWxDatabases", "讀取微信數據庫:"+ now);

        intcount = 0;

        if(msgId != "0") {

            String sql = "select * from message";

            Log.e("sql", sql);

            Cursor c = db.rawQuery(sql, null);

            while(c.moveToNext()) {

                long_id = c.getLong(c.getColumnIndex("msgId"));

                String content = c.getString(c.getColumnIndex("content"));

                inttype = c.getInt(c.getColumnIndex("type"));

                String talker = c.getString(c.getColumnIndex("talker"));

                longtime = c.getLong(c.getColumnIndex("createTime"));

                JSONObject tmpJson = handleJson(_id, content, type, talker, time);

                returnJson.put("data"+ count, tmpJson);

                count++;

            }

            c.close();

            db.close();

            Log.e("readWxDatanases", "讀取結束:"+ System.currentTimeMillis() + ",count:"+ count);

        }

    到此,我們就可以通過自已寫的代碼拿到微信的聊天記錄了,之后可以直接將整理好的JSON通過POST請求發到服務器就可以了。(忍不住吐槽:寫服務器落地程序用了30分鐘,寫上面這一坨花了三四天,還不包括搭建開發環境、下載SDK、折騰ADB什么的)。

    5、獲取QQ聊天記錄過程分享

    5.1 說明

    QQ的聊天記錄有點麻煩,他的文件保存在:“/data/data/com.tencent.mobileqq/databases/你的QQ號碼.db”。

    這個文件是不加密的,可以直接打開。QQ中群組的聊天記錄是單獨建表存放的,所有的QQ群信息存放在TroopInfoV2表里,需要對字段troopuin求MD5,然后找到他的聊天記錄表:mr_troop_" + troopuinMD5 +"_New。

    但是!(看到“但是”就沒好事。。。)

    問題來了,它的內容是加密的,而且加密方法還很復雜:根據手機IMEI循環逐位異或。具體的我不舉例子了,太麻煩,直接看文章最后的解密方法。

    5.2 代碼實現

    第一步,還是拷貝數據庫文件:

    final String QQ_old_path = "/data/data/com.tencent.mobileqq/databases/QQ號.db";

    final String QQ_new_path = "/data/data/com.android.saurfang/QQ號.db";

    DataHelp.copyFile(QQ_old_path,QQ_new_path);

    第二步,打開并讀取內容:

    SQLiteDatabase.loadLibs(context);

    String password = "";

    SQLiteDatabaseHook hook = newSQLiteDatabaseHook() {

        publicvoidpreKey(SQLiteDatabase database) {}

        publicvoidpostKey(SQLiteDatabase database) {

            database.rawExecSQL("PRAGMA cipher_migrate;");

        }

    };

     MessageDecode mDecode = newMessageDecode(imid);

    HashMap<String, String> troopInfo = newHashMap<String, String>();

    try{

        SQLiteDatabase db = openDatabase(newPath,password,null, NO_LOCALIZED_COLLATORS,hook);

        longnow = System.currentTimeMillis();

        Log.e("readQQDatabases","讀取QQ數據庫:"+now);

        //讀取所有的群信息

        String sql = "select troopuin,troopname from TroopInfoV2 where _id";

        Log.e("sql",sql);

        Cursor c = db.rawQuery(sql,null);

        while(c.moveToNext()){

            String troopuin = c.getString(c.getColumnIndex("troopuin"));

            String troopname = c.getString(c.getColumnIndex("troopname"));

            String name = mDecode.nameDecode(troopname);

            String uin = mDecode.uinDecode(troopuin);

            Log.e("readQQDatanases","讀取結束:"+name);

            troopInfo.put(uin, name);

        }

        c.close();


        inttroopCount = troopInfo.size();

        Iterator<String> it = troopInfo.keySet().iterator();

        JSONObject json = newJSONObject();

        //遍歷所有的表

        while(troopCount > 0) {

            try{

                while(it.hasNext()) {

                    String troopuin = (String)it.next();

                    String troopname = troopInfo.get(troopuin);

                    if(troopuin.length() < 8)

                        continue;

                    String troopuinMD5 = getMD5(troopuin);

                    String troopMsgSql = "select _id,msgData, senderuin, time from mr_troop_"+ troopuinMD5 +"_New";

                    Log.e("sql",troopMsgSql);

                    Cursor  cc = db.rawQuery(troopMsgSql,null);

                    JSONObject tmp = newJSONObject();

                    while(cc.moveToNext()) {

                        long_id = cc.getLong(cc.getColumnIndex("_id"));

                        byte[] msgByte = cc.getBlob(cc.getColumnIndex("msgData"));

                        String ss = mDecode.msgDecode(msgByte);

                        //圖片不保留

                        if(ss.indexOf("jpg") != -1|| ss.indexOf("gif") != -1

                                || ss.indexOf("png") != -1)

                            continue;

                        String time = cc.getString(cc.getColumnIndex("time"));

                        String senderuin = cc.getString(cc.getColumnIndex("senderuin"));

                        senderuin  = mDecode.uinDecode(senderuin);

                        JSONObject tmpJson = handleQQJson(_id,ss,senderuin,time);

                        tmp.put(String.valueOf(_id),tmpJson);

                    }

                    troopCount--;

                    cc.close();

                }

            } catch(Exception e) {

                Log.e("e","readWxDatabases"+e.toString());

            }

        }

        db.close();

    }catch(Exception e){

        Log.e("e","readWxDatabases"+e.toString());

    }

    然后你就可以把信息發到服務器落地了(同樣跟微信的記錄上傳一樣,通過你自已寫的代碼發送到你的服務端就可以了)。

    6、題外話:一些注意點

    這里還有幾個需要注意的地方。

    1)最新安卓系統很難寫個死循環直接跑了,所以我們需要使用Intent,來開始Service,再通過Service調用AlarmManager,就像下面的代碼這樣:

    publicclassMainActivity extendsAppCompatActivity {

        privateIntent intent;

        @Override

        protectedvoidonCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity\_main);

            intent = newIntent(this, LongRunningService.class);

            startService(intent);

        }


        @Override

        protectedvoidonDestroy() {

            super.onDestroy();

            stopService(intent);

        }

    }

    然后再創建一個LongRunningService,在其中調用AlarmManager:

    AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);

    intMinutes = 60*1000; //此處規定執行的間隔時間

    longtriggerAtTime = SystemClock.elapsedRealtime() + Minutes;

    Intent intent1 = newIntent(this, AlarmReceiver.class);//注入要執行的類

    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent1, 0);

    manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);

    returnsuper.onStartCommand(intent, flags, startId);

    在AlarmReceiver中調用我們的方法:

    //微信部分

    postWXMsg.readWXDatabase();

    //QQ部分

    postQQMsg.readQQDatabase();

    //再次開啟LongRunningService這個服務,即可實現定時循環。

    Intent intentNext = newIntent(context, LongRunningService.class);

    context.startService(intentNext);

    2)安卓不允許在主線程里進行網絡連接,可以直接用 retrofit2 來發送數據(或者最簡單的方法就是用AsyncTask了)。

    3)項目需要授權網絡連接(就是在AndroidManifast.xml里加上網絡權限申請就是了);

    4)項目需要引入的包:

    implementation files('libs/sqlcipher.jar')

    implementation files('libs/sqlcipher-javadoc.jar')

    implementation 'com.squareup.retrofit2:retrofit:2.0.0'

    implementation 'com.squareup.retrofit2:converter-gson:2.0.0'

    5)如果復制文件時失敗,校驗文件路徑不存在,多半是因為授權問題。需要對數據庫文件授權 全用戶rwx權限;

    6)如果服務端使用MySql數據庫的話,數據庫編碼請用utf8mb4編碼,用來支持Emoji表情。。

    7、我的部分源碼

    (因為種種原因,我不太好直接把源碼貼上來,現把幾個實用方法分享出來,可以直接使用。)

    復制文件的方法:

    /**

      * 復制單個文件

      *

      * @param oldPath String 原文件路徑 如:c:/fqf.txt

      * @param newPath String 復制后路徑 如:f:/fqf.txt

      * @return boolean

      */

     publicstaticbooleancopyFile(String oldPath, String newPath) {

         deleteFolderFile(newPath, true);

         Log.e("copyFile", "time_1:"+ System.currentTimeMillis());

         InputStream inStream = null;

         FileOutputStream fs = null;

         try{

             intbytesum = 0;

             intbyteread = 0;

             File oldfile = newFile(oldPath);

             Boolean flag = oldfile.exists();

             Log.e("copyFile", "flag:"+flag );

             if(oldfile.exists()) { //文件存在時

                 inStream = newFileInputStream(oldPath); //讀入原文件

                 fs = newFileOutputStream(newPath);

                 byte[] buffer = newbyte[2048];

                 while((byteread = inStream.read(buffer)) != -1) {

                     bytesum += byteread; //字節數 文件大小

                     fs.write(buffer, 0, byteread);

                 }

                 Log.e("copyFile", "time_2:"+ System.currentTimeMillis());

             }

         } catch(Exception e) {

             System.out.println("復制單個文件操作出錯");

             e.printStackTrace();

         } finally{

             try{

                 if(inStream != null) {

                     inStream.close();

                 }

                 if(fs != null) {

                     fs.close();

                 }

             } catch(IOException e) {

                 e.printStackTrace();

             }

         }

         returntrue;

     }


     /**

      * 刪除單個文件

      *

      * @param filepath

      * @param deleteThisPath

      */

     publicstaticvoiddeleteFolderFile(String filepath, booleandeleteThisPath) {

         if(!TextUtils.isEmpty(filepath)) {

             try{

                 File file = newFile(filepath);

                 if(file.isDirectory()) {

                     //處理目錄

                     File files[] = file.listFiles();

                     for(inti = 0; i < file.length(); i++) {

                         deleteFolderFile(files[i].getAbsolutePath(), true);

                     }

                 }

                 if(deleteThisPath) {

                     if(!file.isDirectory()) {

                         //刪除文件

                         file.delete();

                     } else{

                         //刪除目錄

                         if(file.listFiles().length == 0) {

                             file.delete();

                         }

                     }

                 }

             } catch(Exception e) {

                 e.printStackTrace();

             }

         }

     }

    MD5方法:

    publicclassMD5Until {

        publicstaticcharHEX_DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

                'A', 'B', 'C', 'D', 'E', 'F'};

        //將字符串轉化為位

        publicstaticString toHexString(byte[] b){

            StringBuilder stringBuilder = newStringBuilder(b.length * 2);

            for(inti = 0; i < b.length; i++) {

                stringBuilder.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);

                stringBuilder.append(HEX_DIGITS[b[i] & 0x0f]);

            }

            returnstringBuilder.toString();

        }

        publicstaticString md5(String string){

            try{

                MessageDigest digest = java.security.MessageDigest.getInstance("MD5");

                digest.update(string.getBytes());

                bytemessageDigest[] = digest.digest();

                returntoHexString(messageDigest);

            }catch(NoSuchAlgorithmException e){

                e.printStackTrace();

            }

            return"";

        }

    }

    QQ信息解密方法:

    public class MessageDecode {

        public String imeiID;

        public intimeiLen;

        public MessageDecode(String imeiID)

        {

            this.imeiID = imeiID;

            this.imeiLen = imeiID.length();

        }

        public boolean isChinese(bytech) {

            intres = ch & 0x80;

            if(res != 0)

                returntrue;

            returnfalse;

        }

        public String timeDecode(String time)

        {

            String datetime = "1970-01-01 08:00:00";

            SimpleDateFormat sdFormat = newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            try{

                longsecond = Long.parseLong(time);

                Date dt = newDate(second * 1000);

                datetime = sdFormat.format(dt);

            } catch(NumberFormatException e) {

                e.printStackTrace();

            }

            returndatetime;

        }

        public String nameDecode(String name)

        {

            bytenbyte[] = name.getBytes();

            byteibyte[] = imeiID.getBytes();

            bytexorName[] = newbyte[nbyte.length];


            intindex = 0;

            for(inti = 0; i < nbyte.length; i++) {

                if(isChinese(nbyte[i])){

                    xorName[i] = nbyte[i];

                    i++;

                    xorName[i] = nbyte[i];

                    i++;

                    xorName[i] = (byte)(nbyte[i] ^ ibyte[index % imeiLen]);

                    index++;

                } else{

                    xorName[i] = (byte)(nbyte[i] ^ ibyte[index % imeiLen]);

                    index++;

                }

            }

            return new String(xorName);

        }

        public String uinDecode(String uin)

        {

            byteubyte[] = uin.getBytes();

            byteibyte[] = imeiID.getBytes();

            bytexorMsg[] = newbyte[ubyte.length];

            intindex = 0;

            for(inti = 0; i < ubyte.length; i++) {

                xorMsg[i] = (byte)(ubyte[i] ^ ibyte[index % imeiLen]);

                index++;

            }

            returnnewString(xorMsg);

        }


        public String msgDecode(byte[] msg)

        {

            byteibyte[] = imeiID.getBytes();

            bytexorMsg[] = newbyte[msg.length];

            intindex = 0;

            for(int i = 0; i < msg.length; i++) {

                xorMsg[i] = (byte)(msg[i] ^ ibyte[index % imeiLen]);

                index++;

            }

            return new String(xorMsg);

        }

    }

    附錄:有關微信、QQ的技術文章匯總

    微信朋友圈千億訪問量背后的技術挑戰和實踐總結

    騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(圖片壓縮篇)

    騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(音視頻技術篇)

    微信團隊分享:微信移動端的全文檢索多音字問題解決方案

    騰訊技術分享:Android版手機QQ的緩存監控與優化實踐

    微信團隊分享:iOS版微信的高性能通用key-value組件技術實踐

    微信團隊分享:iOS版微信是如何防止特殊字符導致的炸群、APP崩潰的?

    騰訊技術分享:Android手Q的線程死鎖監控系統技術實踐

    微信團隊原創分享:iOS版微信的內存監控系統技術實踐

    讓互聯網更快:新一代QUIC協議在騰訊的技術實踐分享

    iOS后臺喚醒實戰:微信收款到賬語音提醒技術總結

    騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路

    微信團隊分享:視頻圖像的超分辨率技術原理和應用場景

    微信團隊分享:微信每日億次實時音視頻聊天背后的技術解密

    QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)

    QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)

    騰訊團隊分享:手機QQ中的人臉識別酷炫動畫效果實現詳解

    騰訊團隊分享 :一次手Q聊天界面中圖片顯示bug的追蹤過程分享

    微信團隊分享:微信Android版小視頻編碼填過的那些坑》 

    微信手機端的本地數據全文檢索優化之路》 

    企業微信客戶端中組織架構數據的同步更新方案優化實戰

    微信團隊披露:微信界面卡死超級bug“15。。。。”的來龍去脈

    QQ 18年:解密8億月活的QQ后臺服務接口隔離技術

    月活8.89億的超級IM微信是如何進行Android端兼容測試的

    以手機QQ為例探討移動端IM中的“輕應用”

    一篇文章get微信開源移動端數據庫組件WCDB的一切!

    微信客戶端團隊負責人技術訪談:如何著手客戶端性能監控和優化

    微信后臺基于時間序的海量數據冷熱分級架構設計實踐

    微信團隊原創分享:Android版微信的臃腫之困與模塊化實踐之路

    微信后臺團隊:微信后臺異步消息隊列的優化升級實踐分享

    微信團隊原創分享:微信客戶端SQLite數據庫損壞修復實踐》 

    騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率》 

    騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》 

    騰訊原創分享(三):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》 

    微信Mars:微信內部正在使用的網絡層封裝庫,即將開源》 

    如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》 

    開源libco庫:單機千萬連接、支撐微信8億用戶的后臺框架基石 [源碼下載]》 

    微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解》 

    微信團隊原創分享:Android版微信后臺保活實戰分享(進程保活篇)》 

    微信團隊原創分享:Android版微信后臺保活實戰分享(網絡保活篇)》 

    Android版微信從300KB到30MB的技術演進(PPT講稿) [附件下載]》 

    微信團隊原創分享:Android版微信從300KB到30MB的技術演進》 

    微信技術總監談架構:微信之道——大道至簡(演講全文)

    微信技術總監談架構:微信之道——大道至簡(PPT講稿) [附件下載]》 

    如何解讀《微信技術總監談架構:微信之道——大道至簡》

    微信海量用戶背后的后臺系統存儲架構(視頻+PPT) [附件下載]

    微信異步化改造實踐:8億月活、單機千萬連接背后的后臺解決方案》 

    微信朋友圈海量技術之道PPT [附件下載]》 

    微信對網絡影響的技術試驗及分析(論文全文)》 

    一份微信后臺技術架構的總結性筆記》 

    架構之道:3個程序員成就微信朋友圈日均10億發布量[有視頻]》 

    快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)

    快速裂變:見證微信強大后臺架構從0到1的演進歷程(二)》 

    微信團隊原創分享:Android內存泄漏監控和優化技巧總結》 

    全面總結iOS版微信升級iOS9遇到的各種“坑”》 

    微信團隊原創資源混淆工具:讓你的APK立減1M》 

    微信團隊原創Android資源混淆工具:AndResGuard [有源碼]》 

    Android版微信安裝包“減肥”實戰記錄》 

    iOS版微信安裝包“減肥”實戰記錄》 

    移動端IM實踐:iOS版微信界面卡頓監測方案》 

    微信“紅包照片”背后的技術難題》 

    移動端IM實踐:iOS版微信小視頻功能技術方案實錄》 

    移動端IM實踐:Android版微信如何大幅提升交互性能(一)

    移動端IM實踐:Android版微信如何大幅提升交互性能(二)

    移動端IM實踐:實現Android版微信的智能心跳機制》 

    移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》 

    移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)

    移動端IM實踐:iOS版微信的多設備字體適配方案探討》 

    信鴿團隊原創:一起走過 iOS10 上消息推送(APNS)的坑

    騰訊信鴿技術分享:百億級實時消息推送的實戰經驗

    IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)

    IPv6技術詳解:基本概念、應用現狀、技術實踐(下篇)

    騰訊TEG團隊原創:基于MySQL的分布式數據庫TDSQL十年鍛造經驗分享

    微信多媒體團隊訪談:音視頻開發的學習、微信的音視頻技術和挑戰等

    了解iOS消息推送一文就夠:史上最全iOS Push技術詳解

    騰訊技術分享:微信小程序音視頻技術背后的故事

    騰訊資深架構師干貨總結:一文讀懂大型分布式系統設計的方方面面

    微信多媒體團隊梁俊斌訪談:聊一聊我所了解的音視頻技術

    騰訊音視頻實驗室:使用AI黑科技實現超低碼率的高清實時視頻聊天

    騰訊技術分享:微信小程序音視頻與WebRTC互通的技術思路和實踐

    手把手教你讀取Android版微信和手Q的聊天記錄(僅作技術研究學習)

    >> 更多同類文章 ……

    (本文同步發布于:http://www.52im.net/thread-1992-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 大妹子影视剧在线观看全集免费| 亚洲国产综合久久天堂| 免费在线观看视频a| 91高清免费国产自产拍2021| 亚洲成a人片7777| 亚洲色大成网站WWW久久九九| 成人永久福利免费观看| 114一级毛片免费| 老司机午夜在线视频免费观| 亚洲avav天堂av在线网爱情| 亚洲黄色高清视频| 亚洲成在人线av| 综合亚洲伊人午夜网| 色片在线免费观看| 亚洲免费精彩视频在线观看| 亚洲砖码砖专无区2023 | 精品无码专区亚洲| 亚洲AⅤ视频一区二区三区| 女人18毛片免费观看| 国国内清清草原免费视频99| 中文字幕在线免费观看| 91精品免费观看| 99免费在线观看视频| 久久国产精品免费专区| 亚洲欧美日韩自偷自拍| 亚洲熟妇无码AV在线播放| 亚洲综合激情另类专区| 国产午夜亚洲精品午夜鲁丝片 | 视频一区二区三区免费观看| 精品无码专区亚洲| 男女污污污超污视频免费在线看| 亚洲色偷偷av男人的天堂| 亚洲综合在线观看视频| 亚洲AⅤ无码一区二区三区在线| 国产色爽女小说免费看| 91福利视频免费| ww4545四虎永久免费地址| 99在线精品免费视频九九视 | 国产成人精品日本亚洲专| 在线观看亚洲AV日韩A∨| 亚洲国产高清国产拍精品|