Jack Jiang

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

          寫在前面

          在著手基于MobileIMSDK開發(fā)自已的即時(shí)通訊應(yīng)用前,建議以Demo工程為腳手架,快速上手MobileIMSDK!

          Demo工程主要用于演示SDK的API調(diào)用等,它位于SDK完整下載包的如下目錄:

          如上圖目錄demo_src/Server/所示,這是一個(gè)完整的IDEA工程(含完整的可運(yùn)行Demo源碼)。

          如果你只是想看看Demo的話,可以下載編譯好的Demo包立即體驗(yàn):它位于SDK完整下載包的 demo_binary/server/ 目錄下。

          第一部分:集成準(zhǔn)備

          第1步:下載SDK并找到lib包

          ① 馬上下載:

          最新版打包下載:Github點(diǎn)此進(jìn)入Gitee同步托管Gitcode同步托管),或者前往 MobileIMSDK的Github自行同步代碼。

          ② 找到lib包:

          位于SDK完整下載包的 sdl_binary/Server/ 目錄下:

          第2步:引用lib包和依賴庫

          提示:MobileIMSDK的Server端lib包支持Java 1.7(含)及以上版本。

          2.1 引用lib包(IDEA工程,本地jar方式)

          ① IDEA中如何引用第3方本地jar包?

          跟所有Java工程一樣引用jar包很簡單,如果沒試過,請查看:IDEA引入本地jar包的兩種方法 或自行百度查找資料。

          ② 以MobileIMSDK Server的Demo工程為例,結(jié)果如下圖:

          2.2 引用lib包(IDEA工程,Maven方式)

          ① IDEA中如何在pom.xml中引用本地jar包和依賴庫?

          在pom.xml加入以下配置:(以下配置參考自IM產(chǎn)品 RainbowChat

          復(fù)制代碼
           1 <dependencies>  2      3   <!-- 依賴的開源JSON庫Gson -->  4   <dependency>  5     <groupId>com.google.code.gson</groupId>  6     <artifactId>gson</artifactId>  7     <version>2.8.9</version>  8   </dependency>  9   10   <!-- 依賴的log4j日志框架的實(shí)用組合 --> 11   <dependency> 12     <groupId>org.apache.logging.log4j</groupId> 13     <artifactId>log4j-api</artifactId> 14     <version>2.23.1</version> 15   </dependency> 16   <dependency> 17     <groupId>org.apache.logging.log4j</groupId> 18     <artifactId>log4j-core</artifactId> 19     <version>2.23.1</version> 20   </dependency> 21   <dependency> 22     <groupId>org.apache.logging.log4j</groupId> 23     <artifactId>log4j-slf4j-impl</artifactId> 24     <version>2.23.1</version> 25   </dependency> 26   27   <!-- 依賴的RabbitMQ中間件的java客戶端庫,用于與MobileIMSDK-Web產(chǎn)品進(jìn)行消息互通時(shí) --> 28   <dependency> 29     <groupId>com.rabbitmq</groupId> 30     <artifactId>amqp-client</artifactId> 31     <version>5.20.0</version> 32   </dependency> 33   34   <!-- 依賴的netty庫 --> 35   <dependency> 36     <groupId>io.netty</groupId> 37     <artifactId>netty-all</artifactId> 38     <version>4.1.67.Final</version> 39   </dependency> 40   41   <!-- MobileIMSDK的核心庫(此jar沒有上傳到mave中央庫,請注意本地引用路徑哦) --> 42   <dependency> 43     <groupId>com.x52im</groupId> 44     <artifactId>mobileimsdk-server</artifactId> 45     <version>6.5</version> 46     <scope>system</scope> 47     <systemPath>${project.basedir}/src/main/webapp/WEB-INF/lib/MobileIMSDKServer.jar</systemPath> 48   </dependency> 49   50 </dependencies>
          復(fù)制代碼

           以RainbowChat的Server工程為例,示例如下圖:

          2.3 引用lib包(Eclipse工程)

          ① Eclipse中如何引用第3方j(luò)ar包?

          跟所有Java工程一樣引用jar包很簡單,如果沒試過,請查看:Eclipse中導(dǎo)入外部jar包 或 Eclipse下導(dǎo)入外部jar包的3種方式。

          ② 以MobileIMSDK Server的Demo工程為例,結(jié)果如下圖:

          第二部分:編寫代碼

          第1步:準(zhǔn)備回調(diào)通知的實(shí)現(xiàn)類

          ① 框架基本事件回調(diào)實(shí)現(xiàn)類:

          復(fù)制代碼
          /**  * 與客服端的所有數(shù)據(jù)交互事件在此ServerEventListener子類中實(shí)現(xiàn)即可。  *   * @author Jack Jiang  * @version 1.0  * @since 3.1  */ public class ServerEventListenerImpl implements ServerEventListener {     private static Logger logger = LoggerFactory.getLogger(ServerEventListenerImpl.class);            /**      * 用戶身份驗(yàn)證回調(diào)方法定義(即驗(yàn)證客戶端連接的合法性,合法就允許正常能信,否則斷開).      * <p>      * 服務(wù)端的應(yīng)用層可在本方法中實(shí)現(xiàn)用戶登陸驗(yàn)證。      * <br>      * 注意:本回調(diào)在一種特殊情況下——即用戶實(shí)際未退出登陸但再次發(fā)起來登陸包時(shí),本回調(diào)是不會(huì)被調(diào)用的!      * <p>      * 根據(jù)MobileIMSDK的算法實(shí)現(xiàn),本方法中用戶驗(yàn)證通過(即方法返回值=0時(shí))后      * ,將立即調(diào)用回調(diào)方法 {@link #onUserLoginSucess(int, String, IoSession)}。      * 否則會(huì)將驗(yàn)證結(jié)果(本方法返回值錯(cuò)誤碼通過客戶端的 ChatBaseEvent.onLoginResponse(int userId, int errorCode)      * 方法進(jìn)行回調(diào))通知客戶端)。      *       * @param userId 傳遞過來的準(zhǔn)一id,保證唯一就可以通信,可能是登陸用戶名、也可能是任意不重復(fù)的id等,具體意義由業(yè)務(wù)層決定      * @param token 用于身份鑒別和合法性檢查的token,它可能是登陸密碼,也可能是通過前置單點(diǎn)登陸接口拿到的token等,具體意義由業(yè)務(wù)層決定      * @param extra 額外信息字符串。本字段目前為保留字段,供上層應(yīng)用自行放置需要的內(nèi)容      * @param session 此客戶端連接對應(yīng)的 netty “會(huì)話”      * @return 0 表示登陸驗(yàn)證通過,否則可以返回用戶自已定義的錯(cuò)誤碼,錯(cuò)誤碼值應(yīng)為:>=1025的整數(shù)      */     @Override     public int onUserLoginVerify(String userId, String token, String extra, Channel session)     {         logger.debug("【DEBUG_回調(diào)通知】正在調(diào)用回調(diào)方法:OnVerifyUserCallBack...(extra="+extra+")");         return 0;     }      /**      * 用戶登錄驗(yàn)證成功后的回調(diào)方法定義(在業(yè)務(wù)上可理解為該用戶的上線通知).      * <p>      * 服務(wù)端的應(yīng)用層通常可在本方法中實(shí)現(xiàn)用戶上線通知等。      * <br>      * 注意:本回調(diào)在一種特殊情況下——即用戶實(shí)際未退出登陸但再次發(fā)起來登陸包時(shí),回調(diào)也是一定會(huì)被調(diào)用。      *       * @param userId 傳遞過來的準(zhǔn)一id,保證唯一就可以通信,可能是登陸用戶名、也可能是任意不重復(fù)的id等,具體意義由業(yè)務(wù)層決定      * @param extra 額外信息字符串。本字段目前為保留字段,供上層應(yīng)用自行放置需要的內(nèi)容。為了豐富應(yīng)用層處理的手段,在本回調(diào)中也把此字段傳進(jìn)來了      * @param session 此客戶端連接對應(yīng)的 netty “會(huì)話”      */     @Override     public void onUserLoginSucess(String userId, String extra, Channel session)     {         logger.debug("【IM_回調(diào)通知onUserLoginSucess】用戶:"+userId+" 上線了!");     }      /**      * 用戶退出登錄回調(diào)方法定義(可理解為下線通知回調(diào))。      * <p>      * 服務(wù)端的應(yīng)用層通??稍诒痉椒ㄖ袑?shí)現(xiàn)用戶下線通知等。      *       * @param userId 下線的用戶user_id      * @param session 此客戶端連接對應(yīng)的 netty “會(huì)話”      * @param beKickoutCode 被踢原因編碼,本參數(shù)當(dāng)為-1時(shí)表示本次logout事件不是源自“被踢”,否則被踢原因編碼請見 {@link PKickoutInfo}類中的常量定義      * @see {@link OnlineProcessor#setBeKickoutCodeForChannel(Channel, int)}      */     @Override     public void onUserLogout(String userId, Channel session, int beKickoutCode)     {         logger.debug("【DEBUG_回調(diào)通知onUserLogout】用戶:"+userId+" 離線了(beKickoutCode="+beKickoutCode+")!");     }          /**      * 收到客戶端發(fā)送給“服務(wù)端”的數(shù)據(jù)回調(diào)通知(即:消息路徑為“C2S”的消息)前的處理邏輯。      * <p>      * <b>本方法的默認(rèn)實(shí)現(xiàn)</b>:<font color="green">當(dāng)開發(fā)者不需要本方法進(jìn)行額外邏輯處理時(shí),請直接返回true即可!</font>      * <p>      * <b>本方法的典型用途</b>:開發(fā)者可在本方法中實(shí)現(xiàn)如:用戶聊天內(nèi)容的鑒黃、過濾、篡改等等,把內(nèi)容審讀權(quán)限交給開發(fā)者,就看怎么用了。      *       * @param p 消息/指令的完整協(xié)議包對象      * @param session 消息發(fā)送者的“會(huì)話”引用(也就是客戶端的網(wǎng)絡(luò)連接對象)      * @return true表示經(jīng)過本方法后將正常進(jìn)入 {@link #onTransferMessage4C2S(Protocal, Channel)}繼續(xù)正常邏輯  ,false表示該條指令將不會(huì)繼續(xù)處理(直接被丟棄)      * @see #onTransferMessage4C2S(Protocal, Channel)      * @since 6.2      */     @Override     public boolean onTransferMessage4C2CBefore(Protocal p, Channel session)     {         return true;     }          /**      * 收到客戶端發(fā)送給“其它客戶端”的數(shù)據(jù)回調(diào)通知(即:消息路徑為“C2C”的消息)前的處理邏輯。      * <p>      * <b>本方法的默認(rèn)實(shí)現(xiàn)</b>:<font color="green">當(dāng)開發(fā)者不需要本方法進(jìn)行額外邏輯處理時(shí),請直接返回true即可!</font>      * <p>      * <b>本方法的典型用途</b>:開發(fā)者可在本方法中實(shí)現(xiàn)如:用戶聊天內(nèi)容的鑒黃、過濾、篡改等等,把內(nèi)容審讀權(quán)限交給開發(fā)者,就看怎么用了。      *       * @param p 消息/指令的完整協(xié)議包對象      * @param session 消息發(fā)送者的“會(huì)話”引用(也就是客戶端的網(wǎng)絡(luò)連接對象)      * @return true表示經(jīng)過本方法后將正常進(jìn)入 {@link #onTransferMessage4C2C(Protocal)}繼續(xù)正常邏輯  ,false表示該條指令將不會(huì)繼續(xù)處理(直接被丟棄)      * @see #onTransferMessage4C2C(Protocal)      * @since 6.2      */     @Override     public boolean onTransferMessage4C2SBefore(Protocal p, Channel session)     {         return true;     }      /**      * 收到客戶端發(fā)送給“服務(wù)端”的數(shù)據(jù)回調(diào)通知(即:消息路徑為“C2S”的消息).      * <p>      * MobileIMSDK在收到客戶端向userId="0"(即接收目標(biāo)是"服務(wù)器")的情況下通過      * 本方法的回調(diào)通知上層。      * <p>      * <b>本方法的典型用途</b>:開發(fā)者通??稍诒痉椒ㄖ袑?shí)現(xiàn)如:添加好友請求等需要服務(wù)端進(jìn)行處理的業(yè)務(wù)。      *       * @param p 消息/指令的完整協(xié)議包對象      * @param session 此客戶端連接對應(yīng)的 netty “會(huì)話”      * @return true表示本方法已成功處理完成,否則表示未處理成功。此返回值目前框架中并沒有特殊意義,僅作保留吧      * @see Protocal      * @since 4.0      */     @Override     public boolean onTransferMessage4C2S(Protocal p, Channel session)     {         // 接收者uid         String userId = p.getTo();         // 發(fā)送者uid         String from_user_id = p.getFrom();         // 消息或指令內(nèi)容         String dataContent = p.getDataContent();         // 消息或指令指紋碼(即唯一ID)         String fingerPrint = p.getFp();         // 【重要】用戶定義的消息或指令協(xié)議類型(開發(fā)者可據(jù)此類型來區(qū)分具體的消息或指令)         int typeu = p.getTypeu();                          logger.debug("【DEBUG_回調(diào)通知】[typeu="+typeu+"]收到了客戶端"+from_user_id+"發(fā)給服務(wù)端的消息:str="+dataContent);         return true;     }      /**      * 收到客戶端發(fā)送給“其它客戶端”的數(shù)據(jù)回調(diào)通知(即:消息路徑為“C2C”的消息).      * <p>      * <b>注意:</b>本方法當(dāng)且僅當(dāng)在數(shù)據(jù)被服務(wù)端成功實(shí)時(shí)發(fā)送(“實(shí)時(shí)”即意味著對方在線的情況下)出去后被回調(diào)調(diào)用.      * <p>      * <b>本方法的典型用途</b>:開發(fā)者可在本方法中可以實(shí)現(xiàn)用戶聊天信息的收集,以便后期監(jiān)控分析用戶的行為等^_^。      * 開發(fā)者可以對本方法不作任何代碼實(shí)現(xiàn),也不會(huì)影響整個(gè)MobileIMSDK的運(yùn)行,因?yàn)楸净卣{(diào)并非關(guān)鍵邏輯,只是個(gè)普通消息傳輸結(jié)果的回調(diào)而已。      * <p>      * 提示:如果開啟消息QoS保證,因重傳機(jī)制,本回調(diào)中的消息理論上有重復(fù)的可能,請以參數(shù) #fingerPrint      * 作為消息的唯一標(biāo)識(shí)ID進(jìn)行去重處理。      *       * @param p 消息/指令的完整協(xié)議包對象      * @see Protocal      * @since 4.0      */     @Override     public void onTransferMessage4C2C(Protocal p)     {         // 接收者uid         String userId = p.getTo();         // 發(fā)送者uid         String from_user_id = p.getFrom();         // 消息或指令內(nèi)容         String dataContent = p.getDataContent();         // 消息或指令指紋碼(即唯一ID)         String fingerPrint = p.getFp();         // 【重要】用戶定義的消息或指令協(xié)議類型(開發(fā)者可據(jù)此類型來區(qū)分具體的消息或指令)         int typeu = p.getTypeu();                          logger.debug("【DEBUG_回調(diào)通知】[typeu="+typeu+"]收到了客戶端"+from_user_id+"發(fā)給客戶端"+userId+"的消息:str="+dataContent);     }      /**      * 服務(wù)端在進(jìn)行消息發(fā)送時(shí),當(dāng)對方在線但實(shí)時(shí)發(fā)送失敗、以及其它各種問題導(dǎo)致消息并沒能正常發(fā)出時(shí)      * ,將無條件走本回調(diào)通知。      *       * <p>      * <b>注意:</b>本方法當(dāng)且僅當(dāng)在數(shù)據(jù)被服務(wù)端<u>在線發(fā)送</u>失敗后被回調(diào)調(diào)用.      *       * <p>      * <b>舉個(gè)例子:以下是一段典型的服務(wù)端消息/指令發(fā)送代碼:</b>      * <pre style="border: 1px solid #eaeaea;background-color: #fff6ea;border-radius: 6px;">      * // 消息接收者的id(這個(gè)id由你自已定義,對于MobileIMSDK來說只要保證唯一性即可)      * String destinationUserId = "400069";      *       * // 這是要發(fā)送的消息("你好"是消息內(nèi)容、“0”是消息發(fā)送者)      * final Protocal p = ProtocalFactory.createCommonData("你好", "0", destinationUserId, true, null, -1);      *           * // 對方在線的情況下,才需要實(shí)時(shí)發(fā)送,否則走離線處理邏輯      * if(OnlineProcessor.isOnline(destinationUserId)) {      *     // netty是異步通知數(shù)據(jù)發(fā)送結(jié)果的      *     MBObserver<Object> resultObserver = new MBObserver<Object>(){      *         public void update(boolean sucess, Object extraObj) {      *             if(sucess){      *                 // 你的消息/指令實(shí)時(shí)發(fā)送成功,不需要額外處理了      *             }      *             else{      *                 //【1】TODO: 你的消息/指令實(shí)時(shí)發(fā)送失敗,在這里實(shí)現(xiàn)離線消息處理邏輯!      *             }      *         }      *     };      *               *     //【2】開始實(shí)時(shí)消息/指令的發(fā)送      *     LocalSendHelper.sendData(p, resultObserver);      * }      * else{      *     //【3】TODO: 你的離線消息處理邏輯!      * }      * <br>      * <font color="#0000ff">如上代碼所示:“【1】【3】”代碼處,開發(fā)者可以自行明確地進(jìn)行離線邏輯處理,“【2】”處如      * 果實(shí)時(shí)發(fā)送時(shí)出現(xiàn)任何問題,將會(huì)走本回調(diào)方法進(jìn)行通知,框架正是通過此回調(diào)進(jìn)一步確保消息可靠性保證的。</font>      * </pre>      * <p>      *       * <p>      * <b>本方法的典型用途</b>:<br>      * 開發(fā)者可在本方法中實(shí)現(xiàn)離線消息的持久化存儲(chǔ)(反正進(jìn)到本回調(diào)通知的消息,就是應(yīng)該被離線存儲(chǔ)起來的)。      *       * <p>      * <b>此方法存的意義何在?</b><br>      * 發(fā)生此種情況的場景可能是:對方確實(shí)不在線(那么此方法里就可以作為離線消息處理了)、或者在發(fā)送時(shí)判斷對方是在線的      * 但服務(wù)端在發(fā)送時(shí)卻沒有成功(這種情況就可能是通信錯(cuò)誤或?qū)Ψ椒钦Mǔ龅形吹竭_(dá)會(huì)話超時(shí)時(shí)限)。<br><u>應(yīng)用層在      * 此方法里實(shí)現(xiàn)離線消息的處理即可!</u>      *       * @param p 消息/指令的完整協(xié)議包對象      * @return true表示應(yīng)用層已經(jīng)處理了離線消息(如果該消息有QoS機(jī)制,則服務(wù)端將代為發(fā)送一條偽應(yīng)答包      * (偽應(yīng)答僅意味著不是接收方的實(shí)時(shí)應(yīng)答,而只是存儲(chǔ)到離線DB中,但在發(fā)送方看來也算是被對方收到,只是延      * 遲收到而已(離線消息嘛))),否則表示應(yīng)用層沒有處理(如果此消息有QoS機(jī)制,則發(fā)送方在QoS重傳機(jī)制超時(shí)      * 后報(bào)出消息發(fā)送失敗的提示)      * @see Protocal      * @see #onTransferMessage4C2C(Protocal)      * @since 4.0      */     @Override     public boolean onTransferMessage_RealTimeSendFaild(Protocal p)     {         // 接收者uid         String userId = p.getTo();         // 發(fā)送者uid         String from_user_id = p.getFrom();         // 消息或指令內(nèi)容         String dataContent = p.getDataContent();         // 消息或指令指紋碼(即唯一ID)         String fingerPrint = p.getFp();         // 【重要】用戶定義的消息或指令協(xié)議類型(開發(fā)者可據(jù)此類型來區(qū)分具體的消息或指令)         int typeu = p.getTypeu();          logger.debug("【DEBUG_回調(diào)通知】[typeu="+typeu+"]客戶端"+from_user_id+"發(fā)給客戶端"+userId+"的消息:str="+dataContent                 +",因?qū)崟r(shí)發(fā)送沒有成功,需要上層應(yīng)用作離線處理哦,否則此消息將被丟棄.");         return false;     }       /**      * <b>注意:</b><font color="red">本回調(diào)僅用于與Web的互通模式下,默認(rèn)情況下本方法可什么也不做,無任何影響。如你對此回調(diào)有疑問可跟Jack Jiang進(jìn)行技術(shù)討論!</font>      * {@inheritDoc}      *       * @since 6.2      */     @Override     public void onTransferMessage4C2C_AfterBridge(Protocal p)     {             // 默認(rèn)本方法可     } }
          復(fù)制代碼

          ② 服務(wù)端主動(dòng)發(fā)起消息的QoS回調(diào)通知實(shí)現(xiàn)類:

          復(fù)制代碼
          /**  * MobileIMSDK的服務(wù)端QoS消息送達(dá)保證機(jī)制的事件監(jiān)聽器實(shí)現(xiàn)類。  * <p>  * <b>當(dāng)前QoS機(jī)制支持全部的C2C、C2S、S2C共3種消息交互場景下的消息送達(dá)質(zhì)量保證:<</b>>  * <ur>  * <li>1) Client to Server(C2S):即由某客戶端主動(dòng)發(fā)起,消息最終接收者是服務(wù)端,此模式下:重發(fā)由C保證、ACK應(yīng)答由S發(fā)回;</li>  * <li>2) Server to Client(S2C):即由服務(wù)端主動(dòng)發(fā)起,消息最終接收者是某客戶端,此模式下:重發(fā)由S保證、ACK應(yīng)答由C發(fā)回;</li>  * <li>2) Client to Client(C2C):即由客戶端主動(dòng)發(fā)起,消息最終接收者是另一客戶端。此模式對于QoS機(jī)制來說,相當(dāng)于C2S+S2C兩程路徑。</li>  * </ul>  * <p>  * TCP理論上能從底層保證數(shù)據(jù)的可靠性,但應(yīng)用層的代碼和場景中存在網(wǎng)絡(luò)本身和網(wǎng)絡(luò)之外的各種不可靠性,  * MobileIMSDK中的QoS送達(dá)保證機(jī)制,將加強(qiáng)TCP的可靠性,確保消息,無法從哪一個(gè)層面和維度,都會(huì)給  * 開發(fā)者提供兩種結(jié)果:要么明確被送達(dá)(即收到ACK應(yīng)答包,見 {@link #messagesBeReceived(String)})  * 、要行明確未被送達(dá)(見 {@link #messagesLost(ArrayList)})。從理論上,保證消息的百分百送達(dá)率。  *   * @author Jack Jiang  * @version 1.0  * @since 3.1  * @see net.x52im.mobileimsdk.server.qos.QoS4SendDaemonS2C  * @see net.x52im.mobileimsdk.server.qos.QoS4ReciveDaemonC2S  * @see MessageQoSEventListenerS2C  */ public class MessageQoSEventS2CListnerImpl implements MessageQoSEventListenerS2C {     private static Logger logger = LoggerFactory.getLogger(MessageQoSEventS2CListnerImpl.class);            /**      * 消息未送達(dá)的回調(diào)事件通知.      *       * @param lostMessages 由MobileIMSDK QoS算法判定出來的未送達(dá)消息列表(此列表      * 中的Protocal對象是原對象的clone(即原對象的深拷貝),請放心使用哦),應(yīng)用層      * 可通過指紋特征碼找到原消息并可以UI上將其標(biāo)記為”發(fā)送失敗“以便即時(shí)告之用戶      */     @Override     public void messagesLost(ArrayList<Protocal> lostMessages)     {         logger.debug("【DEBUG_QoS_S2C事件】收到系統(tǒng)的未實(shí)時(shí)送達(dá)事件通知,當(dāng)前共有"                         +lostMessages.size()+"個(gè)包QoS保證機(jī)制結(jié)束,判定為【無法實(shí)時(shí)送達(dá)】!");     }      /**      * 消息已被對方收到的回調(diào)事件通知.      * <p>      * <b>目前,判定消息被對方收到是有兩種可能:</b><br>      * 1) 對方確實(shí)是在線并且實(shí)時(shí)收到了;<br>      * 2) 對方不在線或者服務(wù)端轉(zhuǎn)發(fā)過程中出錯(cuò)了,由服務(wù)端進(jìn)行離線存儲(chǔ)成功后的反饋      * (此種情況嚴(yán)格來講不能算是“已被收到”,但對于應(yīng)用層來說,離線存儲(chǔ)了的消息      * 原則上就是已送達(dá)了的消息:因?yàn)橛脩粝麓蔚顷憰r(shí)肯定能通過HTTP協(xié)議取到)。      *       * @param theFingerPrint 已被收到的消息的指紋特征碼(唯一ID),應(yīng)用層可據(jù)此ID      * 來找到原先已發(fā)生的消息并可在UI是將其標(biāo)記為”已送達(dá)“或”已讀“以便提升用戶體驗(yàn)      */     @Override     public void messagesBeReceived(String theFingerPrint)     {         if(theFingerPrint != null)         {             logger.debug("【DEBUG_QoS_S2C事件】收到對方已收到消息事件的通知,fp="+theFingerPrint);         }     } }
          復(fù)制代碼

          第2步:服務(wù)端最終配置和實(shí)現(xiàn)

          復(fù)制代碼
          /**  * IM服務(wù)的啟動(dòng)主類。  * <p>  * <b>友情提示:</b>其實(shí)MobileIMSDK的服務(wù)端并非只能以main的主類方式獨(dú)立啟動(dòng),你完全可以把它放到諸如java的Web工程里作為子模塊運(yùn)行,不會(huì)有任何問題!  *   * @author Jack Jiang  * @version 1.0*/ public class ServerLauncherImpl extends ServerLauncher {     private static Logger logger = LoggerFactory.getLogger(ServerLauncherImpl.class);            /**      * 靜態(tài)類方法:進(jìn)行一些全局配置設(shè)置。      */     static     {         // 設(shè)置MobileIMSDK服務(wù)端的UDP網(wǎng)絡(luò)監(jiān)聽端口         GatewayUDP.PORT       = 7901;         // 設(shè)置MobileIMSDK服務(wù)端的TCP網(wǎng)絡(luò)監(jiān)聽端口         GatewayTCP.PORT       = 8901;         // 設(shè)置MobileIMSDK服務(wù)端的WebSocket網(wǎng)絡(luò)監(jiān)聽端口         GatewayWebsocket.PORT = 3000;                  // 設(shè)置MobileIMSDK服務(wù)端僅支持UDP協(xié)議 //        ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_UDP;         // 設(shè)置MobileIMSDK服務(wù)端僅支持TCP協(xié)議 //        ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_TCP;         // 設(shè)置MobileIMSDK服務(wù)端僅支持WebSocket協(xié)議 //        ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_WEBSOCKET;         // 設(shè)置MobileIMSDK服務(wù)端同時(shí)支持UDP、TCP、WebSocket三種協(xié)議         ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_UDP | Gateway.SOCKET_TYPE_TCP | Gateway.SOCKET_TYPE_WEBSOCKET;                  // 開/關(guān)Demog日志的輸出         QoS4SendDaemonS2C.getInstance().setDebugable(true);         QoS4ReciveDaemonC2S.getInstance().setDebugable(true);                  // 與客戶端協(xié)商一致的心跳頻率模式設(shè)置 //        ServerToolKits.setSenseModeUDP(SenseModeUDP.MODE_15S);         ServerToolKits.setSenseModeTCP(SenseModeTCP.MODE_5S);         ServerToolKits.setSenseModeWebsocket(SenseModeWebsocket.MODE_5S); //        ServerToolKits.setSenseModeWebsocket(SenseModeWebsocket.MODE_30S);          // 關(guān)閉與Web端的消息互通橋接器(其實(shí)SDK中默認(rèn)就是false)         ServerLauncher.bridgeEnabled = false;         // TODO 跨服橋接器MQ的URI(本參數(shù)只在ServerLauncher.bridgeEnabled為true時(shí)有意義) //        BridgeProcessor.IMMQ_URI = "amqp://js:19844713@192.168.0.190";                  // 設(shè)置最大TCP幀內(nèi)容長度(不設(shè)置則默認(rèn)最大是 6 * 1024字節(jié)) //        GatewayTCP.TCP_FRAME_MAX_BODY_LENGTH = 60 * 1024;                  SslContext sslContext = createSslContext();         // 開啟TCP協(xié)議的SSL/TLS加密傳輸(請確??蛻舳艘惨验_發(fā)SSL) //        GatewayTCP.sslContext = sslContext;         // 開啟WebSocket協(xié)議的SSL/TLS加密傳輸(請確保SSL證書是正規(guī)CA簽發(fā),否則瀏覽器是不允許的) //        GatewayWebsocket.sslContext = sslContext;     }          /**      * 實(shí)例構(gòu)造方法。      *       * @throws IOException      */     public ServerLauncherImpl() throws IOException     {         super();     }          /**      * 初始化消息處理事件監(jiān)聽者.      */     @Override     protected void initListeners()     {         // ** 設(shè)置各種回調(diào)事件處理實(shí)現(xiàn)類         this.setServerEventListener(new ServerEventListenerImpl());         this.setServerMessageQoSEventListener(new MessageQoSEventS2CListnerImpl());     }          /**      * 創(chuàng)建SslContext對象,用于開啟SSL/TLS加密傳輸。      *       * @return 如果成功創(chuàng)建則返回SslContext對象,否則返回null      */     private static SslContext createSslContext()     {                 try {             /** 示例 1:使用證書(證書位于絕對路徑)*/ //            // 證書文件 //            File certChainFile = new File("c:/certs/netty-cert2.crt"); //            // 證書文件 //            File keyFile = new File("c:/certs/netty-key2.pk8"); //            // 私鑰密碼(注意:Netty只支持.pk8格式,如何生成,見JackJiang文章:) //            String keyPassword = "123456"; //            // 生成SslContext對象(為了方便理解,此處使用的是單向認(rèn)證) //            SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();                              /** 示例 2:使用證書(證書位于相對路徑)*/             // TODO: 注意:請使用自已的證書,Demo中帶的證書為自簽名證書且已綁定域名,不安全?。?!             // 證書文件             InputStream certChainFile = ServerLauncherImpl.class.getResourceAsStream("certs/netty-cert2.crt");             // 私鑰文件(注意:Netty只支持.pk8格式,如何生成,見JackJiang文章:)             InputStream keyFile = ServerLauncherImpl.class.getResourceAsStream("certs/netty-key2.pk8");             // 私鑰密碼(注意:Netty只支持.pk8格式,如何生成,見JackJiang文章:)             String keyPassword = "123456";             // 生成SslContext對象(為了方便理解,此處使用的是單向認(rèn)證)             SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE).build();                              /** 示例 3:使用Netty自帶的自簽名證書(建議該證書僅用于測試使用)*/ //            SelfSignedCertificate ssc = new SelfSignedCertificate(); //            SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();                              return sslCtx;         } catch (Exception e) {              logger.warn("createSslContext()時(shí)出錯(cuò)了,原因:"+e.getMessage(), e);         }                  return null;     }          /**      * Demo程序主入口函數(shù)。      *       * @param args      * @throws Exception      */     public static void main(String[] args) throws Exception      {         // 實(shí)例化后記得startup哦,單獨(dú)startup()的目的是讓調(diào)用者可以延遲決定何時(shí)真正啟動(dòng)IM服務(wù)         final ServerLauncherImpl sli = new ServerLauncherImpl();                  // 啟動(dòng)MobileIMSDK服務(wù)端的Demo         sli.startup();                  // 加一個(gè)鉤子,確保在JVM退出時(shí)釋放netty的資源         Runtime.getRuntime().addShutdownHook(new Thread() {             @Override             public void run() {                 sli.shutdown();             }         });     } }
          復(fù)制代碼

          第三部分:常見開發(fā)問題附錄

          附錄1:可以讓客戶端更省電嗎?

          為了配合Android、iOS客戶端,Server端也需要進(jìn)行設(shè)置。

          請調(diào)用以下API進(jìn)行設(shè)置即可(框架默認(rèn)工作在SenseMode.MODE_15S模式下):

          // MobileIMSDK核心IM框架的服務(wù)端敏感度模式設(shè)置

          ServerLauncherImpl.setSenseMode(SenseMode.MODE_15S);

          MobileIMSDK預(yù)定義了多種模式,詳細(xì)API說明:點(diǎn)此進(jìn)入。

          特別說明:為了保證算法的一致性,以上設(shè)置需所有平臺(tái)客戶端和服務(wù)端都保持一致,否則將發(fā)生不可預(yù)測問題。

          附錄2:服務(wù)端如何向客戶端推送/發(fā)送數(shù)據(jù)(或消息)?

          服務(wù)端使用 LocalSendHelper 類中的sendData系列方法即可,詳見下圖:

          API文檔在線地址:http://docs.52im.net/extend/docs/api/mobileimsdk/server_tcp/

          附錄3:核心庫工程與Demo演示工程的關(guān)系說明

          如下圖所示:從 Github 或 淘寶 得到的核心庫工程和Demo演示工程

          ▲ 左邊為MobileIMSDK的各平臺(tái)核心庫工程,右邊為各平臺(tái)的Demo演示工程

          什么是核心庫工程?

          核心庫工程就是MobileIMSDK的所有框架源碼,它只是個(gè)lib庫,它的作用就像Spring boot、Struts、log4j這些第3庫lib庫一樣:是打成jar包放到您的工程里使用的,您調(diào)用它就能實(shí)現(xiàn)它提供的功能,它自已本身并不能自已運(yùn)行(你不可能讓log4j或Spring boot能雙擊就運(yùn)行吧?)

          什么是Demo演示工程?

          正如“什么是核心庫工程?”一節(jié)所說,MobileIMSDK的核心庫是不能直接運(yùn)行的,它需要打成jar包被您的工程引用并調(diào)用后,才能發(fā)揮它的作用,所以MobileIMSDK的Demo演示工程的目的就是為了告訴你:如何引用MobileIMSDK的核心庫jar包、如何調(diào)用MobileIMSDK的API,讀Demo代碼就知道如何使用它了(所以Demo代碼唯一的意義就是為您演示庫的調(diào)用,別無他用)!

          “我”的工程中使用使用核心庫工程?

          為了方便日后的升級,建議使用MobileIMSDK編譯好的核心庫jar包,當(dāng)然您也可以直接把MobileIMSDK核心庫源庫放到您的工程中(而不是使用編譯好的jar包)。

          您可以在MobileIMSDK的Github如下目錄中找到打包編譯好的jar包:

          附錄4:如何開啟SSL/TLS傳輸加密

          1 您需要準(zhǔn)備一個(gè)SSL/TLS證書(支持自簽名證書)

          可以使用正規(guī)CA機(jī)構(gòu)簽發(fā)的證書,也可以使用自簽名證書,如何生成自簽名證書可自行百度,這方面資料很豐富。

          證書文件就像這樣:

          注:如果你不想買證書,也不知道如何生成自簽名證書,可以跟著這篇文章自已做《手把手教你為基于Netty的IM生成自簽名SSL/TLS證書》。

          2 代碼中啟用SSL/TLS加密

          * 將準(zhǔn)備好的證書放置到服務(wù)端工程的此目錄下(以MobileIMSDK的服務(wù)端Demo工程為例):

           

          * 啟用SSL/TLS配置取消此行代碼注釋即可):

          * 啟用SSL/TLS后的運(yùn)行效果服務(wù)端控制臺(tái)log中出現(xiàn)此字樣即表示SSL/TLS啟用成功!):

          ▲ 服務(wù)端啟動(dòng)時(shí)控制臺(tái)下關(guān)于已開啟TLS/SSL加密的log輸出run.bat運(yùn)行時(shí)))

          (▲ 服務(wù)端啟動(dòng)時(shí)控制臺(tái)下關(guān)于已開啟TLS/SSL加密的log輸出IDEA中運(yùn)行時(shí)))

          ▲ 客戶端發(fā)起WebSocket連接時(shí),服務(wù)端控制臺(tái)下帶有TLS/SSL信息的握手log

          (內(nèi)容參考自:http://www.52im.net/thread-63-1-1.html)



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


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


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 龙岩市| 舟曲县| 松桃| 托克托县| 文水县| 潞西市| 蕉岭县| 合肥市| 子长县| 玛多县| 林口县| 许昌市| 通江县| 红桥区| 冀州市| 武清区| 青岛市| 武冈市| 无为县| 蒙城县| 牙克石市| 苏州市| 达日县| 济源市| 古交市| 昆明市| 徐州市| 陇西县| 无锡市| 双江| 龙陵县| 铁力市| 宜章县| 新河县| 安福县| 南丹县| 永平县| 青河县| 辽源市| 诸暨市| 湖北省|