博客 / 詳情

返回

全平台開源即時通訊IM聊天框架MobileIMSDK的服務端開發指南,支持鴻蒙NEXT

寫在前面
在着手基於MobileIMSDK開發自已的即時通訊應用前,建議以Demo工程為腳手架,快速上手MobileIMSDK!Demo工程主要用於演示SDK的API調用等,它位於SDK完整下載包的如下目錄:
圖片
如上圖目錄demo_src/Server/所示,這是一個完整的IDEA工程(含完整的可運行Demo源碼)。
如果你只是想看看Demo的話,可以下載編譯好的Demo包立即體驗:它位於SDK完整下載包的 demo_binary/server/ 目錄下。

第一部分:集成準備
第1步:下載SDK並找到lib包
① 馬上下載:最新版打包下載:Github點此進入(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工程為例,結果如下圖:
圖片
2.2 引用lib包(IDEA工程,Maven方式)
① IDEA中如何在pom.xml中引用本地jar包和依賴庫?在pom.xml加入以下配置:(以下配置參考自IM產品 RainbowChat)

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日誌框架的實用組合 -->
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產品進行消息互通時 -->
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>

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

2.3 引用lib包(Eclipse工程)
① Eclipse中如何引用第3方jar包?跟所有Java工程一樣引用jar包很簡單,如果沒試過,請查看:Eclipse中導入外部jar包 或 Eclipse下導入外部jar包的3種方式。
② 以MobileIMSDK Server的Demo工程為例,結果如下圖:
圖片

第二部分:編寫代碼第1步:準備回調通知的實現類① 框架基本事件回調實現類:
圖片
/**

  • 與客服端的所有數據交互事件在此ServerEventListener子類中實現即可。
  • @author Jack Jiang
  • @version 1.0
  • @since 3.1
    */

public class ServerEventListenerImpl implements ServerEventListener
{

private static Logger logger = LoggerFactory.getLogger(ServerEventListenerImpl.class);  

/**
 * 用户身份驗證回調方法定義(即驗證客户端連接的合法性,合法就允許正常能信,否則斷開).
 * <p>
 * 服務端的應用層可在本方法中實現用户登陸驗證。
 * <br>
 * 注意:本回調在一種特殊情況下——即用户實際未退出登陸但再次發起來登陸包時,本回調是不會被調用的!
 * <p>
 * 根據MobileIMSDK的算法實現,本方法中用户驗證通過(即方法返回值=0時)後
 * ,將立即調用回調方法 {@link #onUserLoginSucess(int, String, IoSession)}。
 * 否則會將驗證結果(本方法返回值錯誤碼通過客户端的 ChatBaseEvent.onLoginResponse(int userId, int errorCode)
 * 方法進行回調)通知客户端)。
 * 
 * @param userId 傳遞過來的準一id,保證唯一就可以通信,可能是登陸用户名、也可能是任意不重複的id等,具體意義由業務層決定
 * @param token 用於身份鑑別和合法性檢查的token,它可能是登陸密碼,也可能是通過前置單點登陸接口拿到的token等,具體意義由業務層決定
 * @param extra 額外信息字符串。本字段目前為保留字段,供上層應用自行放置需要的內容
 * @param session 此客户端連接對應的 netty “會話”
 * @return 0 表示登陸驗證通過,否則可以返回用户自已定義的錯誤碼,錯誤碼值應為:>=1025的整數
 */
@Override
public int onUserLoginVerify(String userId, String token, String extra, Channel session)
{
    logger.debug("【DEBUG_回調通知】正在調用回調方法:OnVerifyUserCallBack...(extra="+extra+")");
    return 0;
}

/**
 * 用户登錄驗證成功後的回調方法定義(在業務上可理解為該用户的上線通知).
 * <p>
 * 服務端的應用層通常可在本方法中實現用户上線通知等。
 * <br>
 * 注意:本回調在一種特殊情況下——即用户實際未退出登陸但再次發起來登陸包時,回調也是一定會被調用。
 * 
 * @param userId 傳遞過來的準一id,保證唯一就可以通信,可能是登陸用户名、也可能是任意不重複的id等,具體意義由業務層決定
 * @param extra 額外信息字符串。本字段目前為保留字段,供上層應用自行放置需要的內容。為了豐富應用層處理的手段,在本回調中也把此字段傳進來了
 * @param session 此客户端連接對應的 netty “會話”
 */
@Override
public void onUserLoginSucess(String userId, String extra, Channel session)
{
    logger.debug("【IM_回調通知onUserLoginSucess】用户:"+userId+" 上線了!");
}

/**
 * 用户退出登錄回調方法定義(可理解為下線通知回調)。
 * <p>
 * 服務端的應用層通常可在本方法中實現用户下線通知等。
 * 
 * @param userId 下線的用户user_id
 * @param session 此客户端連接對應的 netty “會話”
 * @param beKickoutCode 被踢原因編碼,本參數當為-1時表示本次logout事件不是源自“被踢”,否則被踢原因編碼請見 {@link PKickoutInfo}類中的常量定義
 * @see {@link OnlineProcessor#setBeKickoutCodeForChannel(Channel, int)}
 */
@Override
public void onUserLogout(String userId, Channel session, int beKickoutCode)
{
    logger.debug("【DEBUG_回調通知onUserLogout】用户:"+userId+" 離線了(beKickoutCode="+beKickoutCode+")!");
}

/**
 * 收到客户端發送給“服務端”的數據回調通知(即:消息路徑為“C2S”的消息)前的處理邏輯。
 * <p>
 * <b>本方法的默認實現</b>:<font color="green">當開發者不需要本方法進行額外邏輯處理時,請直接返回true即可!</font>
 * <p>
 * <b>本方法的典型用途</b>:開發者可在本方法中實現如:用户聊天內容的鑑黃、過濾、篡改等等,把內容審讀權限交給開發者,就看怎麼用了。
 * 
 * @param p 消息/指令的完整協議包對象
 * @param session 消息發送者的“會話”引用(也就是客户端的網絡連接對象)
 * @return true表示經過本方法後將正常進入 {@link #onTransferMessage4C2S(Protocal, Channel)}繼續正常邏輯  ,false表示該條指令將不會繼續處理(直接被丟棄)
 * @see #onTransferMessage4C2S(Protocal, Channel)
 * @since 6.2
 */
@Override
public boolean onTransferMessage4C2CBefore(Protocal p, Channel session)
{
    return true;
}

/**
 * 收到客户端發送給“其它客户端”的數據回調通知(即:消息路徑為“C2C”的消息)前的處理邏輯。
 * <p>
 * <b>本方法的默認實現</b>:<font color="green">當開發者不需要本方法進行額外邏輯處理時,請直接返回true即可!</font>
 * <p>
 * <b>本方法的典型用途</b>:開發者可在本方法中實現如:用户聊天內容的鑑黃、過濾、篡改等等,把內容審讀權限交給開發者,就看怎麼用了。
 * 
 * @param p 消息/指令的完整協議包對象
 * @param session 消息發送者的“會話”引用(也就是客户端的網絡連接對象)
 * @return true表示經過本方法後將正常進入 {@link #onTransferMessage4C2C(Protocal)}繼續正常邏輯  ,false表示該條指令將不會繼續處理(直接被丟棄)
 * @see #onTransferMessage4C2C(Protocal)
 * @since 6.2
 */
@Override
public boolean onTransferMessage4C2SBefore(Protocal p, Channel session)
{
    return true;
}

/**
 * 收到客户端發送給“服務端”的數據回調通知(即:消息路徑為“C2S”的消息).
 * <p>
 * MobileIMSDK在收到客户端向userId="0"(即接收目標是"服務器")的情況下通過
 * 本方法的回調通知上層。
 * <p>
 * <b>本方法的典型用途</b>:開發者通常可在本方法中實現如:添加好友請求等需要服務端進行處理的業務。
 * 
 * @param p 消息/指令的完整協議包對象
 * @param session 此客户端連接對應的 netty “會話”
 * @return true表示本方法已成功處理完成,否則表示未處理成功。此返回值目前框架中並沒有特殊意義,僅作保留吧
 * @see Protocal
 * @since 4.0
 */
@Override
public boolean onTransferMessage4C2S(Protocal p, Channel session)
{
    // 接收者uid
    String userId = p.getTo();
    // 發送者uid
    String from_user_id = p.getFrom();
    // 消息或指令內容
    String dataContent = p.getDataContent();
    // 消息或指令指紋碼(即唯一ID)
    String fingerPrint = p.getFp();
    // 【重要】用户定義的消息或指令協議類型(開發者可據此類型來區分具體的消息或指令)
    int typeu = p.getTypeu();
            
    logger.debug("【DEBUG_回調通知】[typeu="+typeu+"]收到了客户端"+from_user_id+"發給服務端的消息:str="+dataContent);
    return true;
}

/**
 * 收到客户端發送給“其它客户端”的數據回調通知(即:消息路徑為“C2C”的消息).
 * <p>
 * <b>注意:</b>本方法當且僅當在數據被服務端成功實時發送(“實時”即意味着對方在線的情況下)出去後被回調調用.
 * <p>
 * <b>本方法的典型用途</b>:開發者可在本方法中可以實現用户聊天信息的收集,以便後期監控分析用户的行為等^_^。
 * 開發者可以對本方法不作任何代碼實現,也不會影響整個MobileIMSDK的運行,因為本回調並非關鍵邏輯,只是個普通消息傳輸結果的回調而已。
 * <p>
 * 提示:如果開啓消息QoS保證,因重傳機制,本回調中的消息理論上有重複的可能,請以參數 #fingerPrint
 * 作為消息的唯一標識ID進行去重處理。
 * 
 * @param p 消息/指令的完整協議包對象
 * @see Protocal
 * @since 4.0
 */
@Override
public void onTransferMessage4C2C(Protocal p)
{
    // 接收者uid
    String userId = p.getTo();
    // 發送者uid
    String from_user_id = p.getFrom();
    // 消息或指令內容
    String dataContent = p.getDataContent();
    // 消息或指令指紋碼(即唯一ID)
    String fingerPrint = p.getFp();
    // 【重要】用户定義的消息或指令協議類型(開發者可據此類型來區分具體的消息或指令)
    int typeu = p.getTypeu();
            
    logger.debug("【DEBUG_回調通知】[typeu="+typeu+"]收到了客户端"+from_user_id+"發給客户端"+userId+"的消息:str="+dataContent);
}

/**
 * 服務端在進行消息發送時,當對方在線但實時發送失敗、以及其它各種問題導致消息並沒能正常發出時
 * ,將無條件走本回調通知。
 * 
 * <p>
 * <b>注意:</b>本方法當且僅當在數據被服務端<u>在線發送</u>失敗後被回調調用.
 * 
 * <p>
 * <b>舉個例子:以下是一段典型的服務端消息/指令發送代碼:</b>
 * <pre style="border: 1px solid #eaeaea;background-color: #fff6ea;border-radius: 6px;">
 * // 消息接收者的id(這個id由你自已定義,對於MobileIMSDK來説只要保證唯一性即可)
 * String destinationUserId = "400069";
 * 
 * // 這是要發送的消息("你好"是消息內容、“0”是消息發送者)
 * final Protocal p = ProtocalFactory.createCommonData("你好", "0", destinationUserId, true, null, -1);
 *     
 * // 對方在線的情況下,才需要實時發送,否則走離線處理邏輯
 * if(OnlineProcessor.isOnline(destinationUserId)) {
 *     // netty是異步通知數據發送結果的
 *     MBObserver<Object> resultObserver = new MBObserver<Object>(){
 *         public void update(boolean sucess, Object extraObj) {
 *             if(sucess){
 *                 // 你的消息/指令實時發送成功,不需要額外處理了
 *             }
 *             else{
 *                 //【1】TODO: 你的消息/指令實時發送失敗,在這裏實現離線消息處理邏輯!
 *             }
 *         }
 *     };
 *         
 *     //【2】開始實時消息/指令的發送
 *     LocalSendHelper.sendData(p, resultObserver);
 * }
 * else{
 *     //【3】TODO: 你的離線消息處理邏輯!
 * }
 * <br>
 * <font color="#0000ff">如上代碼所示:“【1】【3】”代碼處,開發者可以自行明確地進行離線邏輯處理,“【2】”處如
 * 果實時發送時出現任何問題,將會走本回調方法進行通知,框架正是通過此回調進一步確保消息可靠性保證的。</font>
 * </pre>
 * <p>
 * 
 * <p>
 * <b>本方法的典型用途</b>:<br>
 * 開發者可在本方法中實現離線消息的持久化存儲(反正進到本回調通知的消息,就是應該被離線存儲起來的)。
 * 
 * <p>
 * <b>此方法存的意義何在?</b><br>
 * 發生此種情況的場景可能是:對方確實不在線(那麼此方法裏就可以作為離線消息處理了)、或者在發送時判斷對方是在線的
 * 但服務端在發送時卻沒有成功(這種情況就可能是通信錯誤或對方非正常通出但尚未到達會話超時時限)。<br><u>應用層在
 * 此方法裏實現離線消息的處理即可!</u>
 * 
 * @param p 消息/指令的完整協議包對象
 * @return true表示應用層已經處理了離線消息(如果該消息有QoS機制,則服務端將代為發送一條偽應答包
 * (偽應答僅意味着不是接收方的實時應答,而只是存儲到離線DB中,但在發送方看來也算是被對方收到,只是延
 * 遲收到而已(離線消息嘛))),否則表示應用層沒有處理(如果此消息有QoS機制,則發送方在QoS重傳機制超時
 * 後報出消息發送失敗的提示)
 * @see Protocal
 * @see #onTransferMessage4C2C(Protocal)
 * @since 4.0
 */
@Override
public boolean onTransferMessage_RealTimeSendFaild(Protocal p)
{
    // 接收者uid
    String userId = p.getTo();
    // 發送者uid
    String from_user_id = p.getFrom();
    // 消息或指令內容
    String dataContent = p.getDataContent();
    // 消息或指令指紋碼(即唯一ID)
    String fingerPrint = p.getFp();
    // 【重要】用户定義的消息或指令協議類型(開發者可據此類型來區分具體的消息或指令)
    int typeu = p.getTypeu();

    logger.debug("【DEBUG_回調通知】[typeu="+typeu+"]客户端"+from_user_id+"發給客户端"+userId+"的消息:str="+dataContent
            +",因實時發送沒有成功,需要上層應用作離線處理哦,否則此消息將被丟棄.");
    return false;
}


/**
 * <b>注意:</b><font color="red">本回調僅用於與Web的互通模式下,默認情況下本方法可什麼也不做,無任何影響。如你對此回調有疑問可跟Jack Jiang進行技術討論!</font>
 * {@inheritDoc}
 * 
 * @since 6.2
 */
@Override
public void onTransferMessage4C2C_AfterBridge(Protocal p)
{    
    // 默認本方法可
}

}

② 服務端主動發起消息的QoS回調通知實現類:
圖片
/**

  • MobileIMSDK的服務端QoS消息送達保證機制的事件監聽器實現類。
  • <p>
  • 當前QoS機制支持全部的C2C、C2S、S2C共3種消息交互場景下的消息送達質量保證:<>
  • <ur>
  • <li>1) Client to Server(C2S):即由某客户端主動發起,消息最終接收者是服務端,此模式下:重發由C保證、ACK應答由S發回;</li>
  • <li>2) Server to Client(S2C):即由服務端主動發起,消息最終接收者是某客户端,此模式下:重發由S保證、ACK應答由C發回;</li>
  • <li>2) Client to Client(C2C):即由客户端主動發起,消息最終接收者是另一客户端。此模式對於QoS機制來説,相當於C2S+S2C兩程路徑。</li>
  • </ul>
  • <p>
  • TCP理論上能從底層保證數據的可靠性,但應用層的代碼和場景中存在網絡本身和網絡之外的各種不可靠性,
  • MobileIMSDK中的QoS送達保證機制,將加強TCP的可靠性,確保消息,無法從哪一個層面和維度,都會給
  • 開發者提供兩種結果:要麼明確被送達(即收到ACK應答包,見 {@link #messagesBeReceived(String)})
  • 、要行明確未被送達(見 {@link #messagesLost(ArrayList)})。從理論上,保證消息的百分百送達率。
  • @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);  

/**
 * 消息未送達的回調事件通知.
 * 
 * @param lostMessages 由MobileIMSDK QoS算法判定出來的未送達消息列表(此列表
 * 中的Protocal對象是原對象的clone(即原對象的深拷貝),請放心使用哦),應用層
 * 可通過指紋特徵碼找到原消息並可以UI上將其標記為”發送失敗“以便即時告之用户
 */
@Override
public void messagesLost(ArrayList<Protocal> lostMessages)
{
    logger.debug("【DEBUG_QoS_S2C事件】收到系統的未實時送達事件通知,當前共有"
                    +lostMessages.size()+"個包QoS保證機制結束,判定為【無法實時送達】!");
}

/**
 * 消息已被對方收到的回調事件通知.
 * <p>
 * <b>目前,判定消息被對方收到是有兩種可能:</b><br>
 * 1) 對方確實是在線並且實時收到了;<br>
 * 2) 對方不在線或者服務端轉發過程中出錯了,由服務端進行離線存儲成功後的反饋
 * (此種情況嚴格來講不能算是“已被收到”,但對於應用層來説,離線存儲了的消息
 * 原則上就是已送達了的消息:因為用户下次登陸時肯定能通過HTTP協議取到)。
 * 
 * @param theFingerPrint 已被收到的消息的指紋特徵碼(唯一ID),應用層可據此ID
 * 來找到原先已發生的消息並可在UI是將其標記為”已送達“或”已讀“以便提升用户體驗
 */
@Override
public void messagesBeReceived(String theFingerPrint)
{
    if(theFingerPrint != null)
    {
        logger.debug("【DEBUG_QoS_S2C事件】收到對方已收到消息事件的通知,fp="+theFingerPrint);
    }
}

}
圖片
第2步:服務端最終配置和實現
圖片
/**

  • IM服務的啓動主類。
  • <p>
  • 友情提示:其實MobileIMSDK的服務端並非只能以main的主類方式獨立啓動,你完全可以把它放到諸如java的Web工程裏作為子模塊運行,不會有任何問題!
  • @author Jack Jiang
  • @version 1.0*/

public class ServerLauncherImpl extends ServerLauncher
{

private static Logger logger = LoggerFactory.getLogger(ServerLauncherImpl.class);  

/**
 * 靜態類方法:進行一些全局配置設置。
 */
static
{
    // 設置MobileIMSDK服務端的UDP網絡監聽端口
    GatewayUDP.PORT       = 7901;
    // 設置MobileIMSDK服務端的TCP網絡監聽端口
    GatewayTCP.PORT       = 8901;
    // 設置MobileIMSDK服務端的WebSocket網絡監聽端口
    GatewayWebsocket.PORT = 3000;
    
    // 設置MobileIMSDK服務端僅支持UDP協議

// ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_UDP;

    // 設置MobileIMSDK服務端僅支持TCP協議

// ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_TCP;

    // 設置MobileIMSDK服務端僅支持WebSocket協議

// ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_WEBSOCKET;

    // 設置MobileIMSDK服務端同時支持UDP、TCP、WebSocket三種協議
    ServerLauncher.supportedGateways = Gateway.SOCKET_TYPE_UDP | Gateway.SOCKET_TYPE_TCP | Gateway.SOCKET_TYPE_WEBSOCKET;
    
    // 開/關Demog日誌的輸出
    QoS4SendDaemonS2C.getInstance().setDebugable(true);
    QoS4ReciveDaemonC2S.getInstance().setDebugable(true);
    
    // 與客户端協商一致的心跳頻率模式設置

// ServerToolKits.setSenseModeUDP(SenseModeUDP.MODE_15S);

    ServerToolKits.setSenseModeTCP(SenseModeTCP.MODE_5S);
    ServerToolKits.setSenseModeWebsocket(SenseModeWebsocket.MODE_5S);

// ServerToolKits.setSenseModeWebsocket(SenseModeWebsocket.MODE_30S);

    // 關閉與Web端的消息互通橋接器(其實SDK中默認就是false)
    ServerLauncher.bridgeEnabled = false;
    // TODO 跨服橋接器MQ的URI(本參數只在ServerLauncher.bridgeEnabled為true時有意義)

// BridgeProcessor.IMMQ_URI = "amqp://js:19844713@192.168.0.190";

    
    // 設置最大TCP幀內容長度(不設置則默認最大是 6 * 1024字節)

// GatewayTCP.TCP_FRAME_MAX_BODY_LENGTH = 60 * 1024;

    
    SslContext sslContext = createSslContext();
    // 開啓TCP協議的SSL/TLS加密傳輸(請確保客户端也已開發SSL)

// GatewayTCP.sslContext = sslContext;

    // 開啓WebSocket協議的SSL/TLS加密傳輸(請確保SSL證書是正規CA簽發,否則瀏覽器是不允許的)

// GatewayWebsocket.sslContext = sslContext;

}

/**
 * 實例構造方法。
 * 
 * @throws IOException
 */
public ServerLauncherImpl() throws IOException
{
    super();
}

/**
 * 初始化消息處理事件監聽者.
 */
@Override
protected void initListeners()
{
    // ** 設置各種回調事件處理實現類
    this.setServerEventListener(new ServerEventListenerImpl());
    this.setServerMessageQoSEventListener(new MessageQoSEventS2CListnerImpl());
}

/**
 * 創建SslContext對象,用於開啓SSL/TLS加密傳輸。
 * 
 * @return 如果成功創建則返回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對象(為了方便理解,此處使用的是單向認證)
// 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對象(為了方便理解,此處使用的是單向認證)
        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()時出錯了,原因:"+e.getMessage(), e);
    }
    
    return null;
}

/**
 * Demo程序主入口函數。
 * 
 * @param args
 * @throws Exception
 */
public static void main(String[] args) throws Exception 
{
    // 實例化後記得startup哦,單獨startup()的目的是讓調用者可以延遲決定何時真正啓動IM服務
    final ServerLauncherImpl sli = new ServerLauncherImpl();
    
    // 啓動MobileIMSDK服務端的Demo
    sli.startup();
    
    // 加一個鈎子,確保在JVM退出時釋放netty的資源
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            sli.shutdown();
        }
    });
}

}

第三部分:常見開發問題附錄
附錄1:可以讓客户端更省電嗎?
為了配合Android、iOS客户端,Server端也需要進行設置。
請調用以下API進行設置即可(框架默認工作在SenseMode.MODE_15S模式下):
// MobileIMSDK核心IM框架的服務端敏感度模式設置ServerLauncherImpl.setSenseMode(SenseMode.MODE_15S);MobileIMSDK預定義了多種模式,詳細API説明:點此進入。特別説明:為了保證算法的一致性,以上設置需所有平台客户端和服務端都保持一致,否則將發生不可預測問題。

附錄2:服務端如何向客户端推送/發送數據(或消息)?服務端使用 LocalSendHelper 類中的sendData系列方法即可,詳見下圖:
圖片
API文檔在線地址:http://docs.52im.net/extend/docs/api/mobileimsdk/server_tcp/

附錄3:核心庫工程與Demo演示工程的關係説明如下圖所示:從 Github 或 淘寶 得到的核心庫工程和Demo演示工程
圖片
(▲ 左邊為MobileIMSDK的各平台核心庫工程,右邊為各平台的Demo演示工程)

* 什麼是核心庫工程?
核心庫工程就是MobileIMSDK的所有框架源碼,它只是個lib庫,它的作用就像Spring boot、Struts、log4j這些第3庫lib庫一樣:是打成jar包放到您的工程裏使用的,您調用它就能實現它提供的功能,它自已本身並不能自已運行(你不可能讓log4j或Spring boot能雙擊就運行吧?)

* 什麼是Demo演示工程?
正如“什麼是核心庫工程?”一節所説,MobileIMSDK的核心庫是不能直接運行的,它需要打成jar包被您的工程引用並調用後,才能發揮它的作用,所以MobileIMSDK的Demo演示工程的目的就是為了告訴你:如何引用MobileIMSDK的核心庫jar包、如何調用MobileIMSDK的API,讀Demo代碼就知道如何使用它了(所以Demo代碼唯一的意義就是為您演示庫的調用,別無他用)!
* “我”的工程中使用使用核心庫工程?為了方便日後的升級,建議使用MobileIMSDK編譯好的核心庫jar包,當然您也可以直接把MobileIMSDK核心庫源庫放到您的工程中(而不是使用編譯好的jar包)。
* 您可以在MobileIMSDK的Github如下目錄中找到打包編譯好的jar包:
圖片

附錄4:如何開啓SSL/TLS傳輸加密
1 您需要準備一個SSL/TLS證書(支持自簽名證書)可以使用正規CA機構簽發的證書,也可以使用自簽名證書,如何生成自簽名證書可自行百度,這方面資料很豐富。證書文件就像這樣:
圖片
注:如果你不想買證書,也不知道如何生成自簽名證書,可以跟着這篇文章自已做《手把手教你為基於Netty的IM生成自簽名SSL/TLS證書》。
2 代碼中啓用SSL/TLS加密* 將準備好的證書放置到服務端工程的此目錄下(以MobileIMSDK的服務端Demo工程為例):
圖片

圖片
 * 啓用SSL/TLS配置(取消此行代碼註釋即可):
圖片

  • 啓用SSL/TLS後的運行效果(服務端控制枱log中出現此字樣即表示SSL/TLS啓用成功!):
    圖片
    (▲ 服務端啓動時控制枱下關於已開啓TLS/SSL加密的log輸出(run.bat運行時))
    圖片
    (▲ 服務端啓動時控制枱下關於已開啓TLS/SSL加密的log輸出(IDEA中運行時))
    圖片
    (▲ 客户端發起WebSocket連接時,服務端控制枱下帶有TLS/SSL信息的握手log)

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

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.