SpringBoot(22) 集成MobileIMSDK實現(xiàn)即時通訊

一郎笆、前言

MobileIMSDK是什么?

一個專為移動端開發(fā)的開源原創(chuàng)即時通訊框架蚤吹,超輕量級、高度提煉氧猬,完全基于UDP協(xié)議柱锹,支持iOSAndroid有巧、標(biāo)準(zhǔn)Java平臺,服務(wù)端基于MinaNetty編寫譬胎。MobileIMSDK還可與姊妹工程 MobileIMSDK-Web無縫互通,從而實現(xiàn)Web網(wǎng)頁端聊天推送等沈条。

本文將實現(xiàn)
  1. 基于springboot2.1.8.RELEASE 集成 MobileIMSDK
  2. 開發(fā)IM服務(wù)端
  3. 開發(fā)客戶端
  4. 實現(xiàn)Java客戶端與客戶端之間的通信

二、SpringBoot 集成 MobileIMSDK 準(zhǔn)備

1憨攒、MobileIMSDK下載:https://gitee.com/jackjiang/MobileIMSDK

  1. 服務(wù)端所需jar包: dist/server-xxx
  2. 客服端所需jar包: dist/client/java
    在這里插入圖片描述

2伐憾、pom.xml中引入相關(guān)依賴

由于這里是maven項目,其中一部分jar包可通過maven倉庫直接引入,而其余的則通過外部jar包引入方式使用即可~

如下4個需作為外部jar包在pom.xml中引入


在這里插入圖片描述
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.5</version>
</dependency>

<!-- MobileIMSDK所需jar包依賴[注:這里是在本地lib中引入芍躏,maven中央倉庫中暫無此jar包]姐刁,要與<includeSystemScope>true</includeSystemScope>配合使用-->
<dependency>
    <groupId>com.zhengqing</groupId>
    <artifactId>MobileIMSDK4j</artifactId>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/lib/MobileIMSDK4j.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.zhengqing</groupId>
    <artifactId>MobileIMSDKServerX_meta</artifactId>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/lib/MobileIMSDKServerX_meta.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.zhengqing</groupId>
    <artifactId>swing-worker-1.2(1.6-)</artifactId>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/lib/swing-worker-1.2(1.6-).jar</systemPath>
</dependency>
<dependency>
    <groupId>com.zhengqing</groupId>
    <artifactId>MobileIMSDKServerX_netty</artifactId>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/lib/MobileIMSDKServerX_netty.jar</systemPath>
</dependency>
<plugins>
    <!-- maven打包插件 -> 將整個工程打成一個 fatjar -->
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <!-- 作用:項目打成jar钥勋,同時把本地jar包也引入進(jìn)去 -->
        <configuration>
            <includeSystemScope>true</includeSystemScope>
        </configuration>
    </plugin>
</plugins>

三、開發(fā)服務(wù)端

在這里插入圖片描述

1巧涧、與客服端的所有數(shù)據(jù)交互事件(實現(xiàn)ServerEventListener類)

public class ServerEventListenerImpl implements ServerEventListener {
    private static Logger logger = LoggerFactory.getLogger(ServerEventListenerImpl.class);

    /**
     * 用戶身份驗證回調(diào)方法定義.
     * <p>
     * 服務(wù)端的應(yīng)用層可在本方法中實現(xiàn)用戶登陸驗證歪脏。
     * <br>
     * 注意:本回調(diào)在一種特殊情況下——即用戶實際未退出登陸但再次發(fā)起來登陸包時挺物,本回調(diào)是不會被調(diào)用的赶撰!
     * <p>
     * 根據(jù)MobileIMSDK的算法實現(xiàn)吓蘑,本方法中用戶驗證通過(即方法返回值=0時)后
     * 伟叛,將立即調(diào)用回調(diào)方法 {@link #onUserLoginAction_CallBack(int, String, IoSession)}鞭衩。
     * 否則會將驗證結(jié)果(本方法返回值錯誤碼通過客戶端的 ChatBaseEvent.onLoginMessage(int dwUserId, int dwErrorCode)
     * 方法進(jìn)行回調(diào))通知客戶端)。
     *
     * @param userId  傳遞過來的準(zhǔn)一id滥搭,保證唯一就可以通信外厂,可能是登陸用戶名、也可能是任意不重復(fù)的id等针肥,具體意義由業(yè)務(wù)層決定
     * @param token   用于身份鑒別和合法性檢查的token,它可能是登陸密碼,也可能是通過前置單點登陸接口拿到的token等,具體意義由業(yè)務(wù)層決定
     * @param extra   額外信息字符串采记。本字段目前為保留字段,供上層應(yīng)用自行放置需要的內(nèi)容
     * @param session 此客戶端連接對應(yīng)的 netty “會話”
     * @return 0 表示登陸驗證通過,否則可以返回用戶自已定義的錯誤碼丸冕,錯誤碼值應(yīng)為:>=1025的整數(shù)
     */
    @Override
    public int onVerifyUserCallBack(String userId, String token, String extra, Channel session) {
        logger.debug("【DEBUG_回調(diào)通知】正在調(diào)用回調(diào)方法:OnVerifyUserCallBack...(extra=" + extra + ")");
        return 0;
    }

    /**
     * 用戶登錄驗證成功后的回調(diào)方法定義(可理解為上線通知回調(diào)).
     * <p>
     * 服務(wù)端的應(yīng)用層通车⒚罚可在本方法中實現(xiàn)用戶上線通知等。
     * <br>
     * 注意:本回調(diào)在一種特殊情況下——即用戶實際未退出登陸但再次發(fā)起來登陸包時胖烛,回調(diào)也是一定會被調(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 “會話”
     */
    @Override
    public void onUserLoginAction_CallBack(String userId, String extra, Channel session) {
        logger.debug("【IM_回調(diào)通知OnUserLoginAction_CallBack】用戶:" + userId + " 上線了胃榕!");
    }

    /**
     * 用戶退出登錄回調(diào)方法定義(可理解為下線通知回調(diào))。
     * <p>
     * 服務(wù)端的應(yīng)用層通车莨澹可在本方法中實現(xiàn)用戶下線通知等您朽。
     *
     * @param userId  下線的用戶user_id
     * @param obj
     * @param session 此客戶端連接對應(yīng)的 netty “會話”
     */
    @Override
    public void onUserLogoutAction_CallBack(String userId, Object obj, Channel session) {
        logger.debug("【DEBUG_回調(diào)通知OnUserLogoutAction_CallBack】用戶:" + userId + " 離線了县习!");
    }

    /**
     * 通用數(shù)據(jù)回調(diào)方法定義(客戶端發(fā)給服務(wù)端的(即接收user_id="0")).
     * <p>
     * MobileIMSDK在收到客戶端向user_id=0(即接收目標(biāo)是服務(wù)器)的情況下通過
     * 本方法的回調(diào)通知上層。上層通匙剩可在本方法中實現(xiàn)如:添加好友請求等業(yè)務(wù)實現(xiàn)。
     *
     * <p style="background:#fbf5ee;border-radius:4px;">
     * <b><font color="#ff0000">【版本兼容性說明】</font></b>本方法用于替代v3.x中的以下方法:<br>
     * <code>public boolean onTransBuffer_CallBack(String userId, String from_user_id
     * , String dataContent, String fingerPrint, int typeu, Channel session);
     * </code>
     *
     * @param userId       接收方的user_id(本方法接收的是發(fā)給服務(wù)端的消息备埃,所以此參數(shù)的值肯定==0)
     * @param from_user_id 發(fā)送方的user_id
     * @param dataContent  數(shù)據(jù)內(nèi)容(文本形式)
     * @param session      此客戶端連接對應(yīng)的 netty “會話”
     * @return true表示本方法已成功處理完成,否則表示未處理成功。此返回值目前框架中并沒有特殊意義吹菱,僅作保留吧
     * @since 4.0
     */
    @Override
    public boolean onTransBuffer_C2S_CallBack(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;
    }

    /**
     * 通道數(shù)據(jù)回調(diào)函數(shù)定義(客戶端發(fā)給客戶端的(即接收方user_id不為“0”的情況)).
     * <p>
     * <b>注意:</b>本方法當(dāng)且僅當(dāng)在數(shù)據(jù)被服務(wù)端成功在線發(fā)送出去后被回調(diào)調(diào)用.
     * <p>
     * 上層通常可在本方法中實現(xiàn)用戶聊天信息的收集,以便后期監(jiān)控分析用戶的行為等^_^奶镶。
     * <p>
     * 提示:如果開啟消息QoS保證,因重傳機(jī)制舅世,本回調(diào)中的消息理論上有重復(fù)的可能长踊,請以參數(shù) #fingerPrint
     * 作為消息的唯一標(biāo)識ID進(jìn)行去重處理。
     *
     * <p style="background:#fbf5ee;border-radius:4px;">
     * <b><font color="#ff0000">【版本兼容性說明】</font></b>本方法用于替代v3.x中的以下方法:<br>
     * <code>public void onTransBuffer_C2C_CallBack(String userId, String from_user_id
     * , String dataContent, String fingerPrint, int typeu);
     *
     * @param userId       接收方的user_id(本方法接收的是客戶端發(fā)給客戶端的,所以此參數(shù)的值肯定>0)
     * @param from_user_id 發(fā)送方的user_id
     * @param dataContent
     * @since 4.0
     */
    @Override
    public void onTransBuffer_C2C_CallBack(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);
    }

    /**
     * 通用數(shù)據(jù)實時發(fā)送失敗后的回調(diào)函數(shù)定義(客戶端發(fā)給客戶端的(即接收方user_id不為“0”的情況)).
     * <p>
     * 注意:本方法當(dāng)且僅當(dāng)在數(shù)據(jù)被服務(wù)端<u>在線發(fā)送</u>失敗后被回調(diào)調(diào)用.
     * <p>
     * <b>此方法存的意義何在?</b><br>
     * 發(fā)生此種情況的場景可能是:對方確實不在線(那么此方法里就可以作為離線消息處理了)、
     * 或者在發(fā)送時判斷對方是在線的但服務(wù)端在發(fā)送時卻沒有成功(這種情況就可能是通信錯誤
     * 或?qū)Ψ椒钦Mǔ龅形吹竭_(dá)會話超時時限)。<br><u>應(yīng)用層在此方法里實現(xiàn)離線消息的處理即可!</u>
     *
     * <p style="background:#fbf5ee;border-radius:4px;">
     * <b><font color="#ff0000">【版本兼容性說明】</font></b>本方法用于替代v3.x中的以下方法:<br>
     * <code>public boolean onTransBuffer_C2C_RealTimeSendFaild_CallBack(String userId
     * , String from_user_id, String dataContent, String fingerPrint, int typeu);
     * </code>
     *
     * @param userId       接收方的user_id(本方法接收的是客戶端發(fā)給客戶端的逐工,所以此參數(shù)的值肯定>0)摘仅,此id在本方法中不一定保證有意義
     * @param from_user_id 發(fā)送方的user_id
     * @param dataContent  消息內(nèi)容
     * @param fingerPrint  該消息對應(yīng)的指紋(如果該消息有QoS保證機(jī)制的話)恬吕,用于在QoS重要機(jī)制下服務(wù)端離線存儲時防止重復(fù)存儲哦
     * @return true表示應(yīng)用層已經(jīng)處理了離線消息(如果該消息有QoS機(jī)制豺旬,則服務(wù)端將代為發(fā)送一條偽應(yīng)答包
     * (偽應(yīng)答僅意味著不是接收方的實時應(yīng)答泳猬,而只是存儲到離線DB中,但在發(fā)送方看來也算是被對方收到忙上,只是延
     * 遲收到而已(離線消息嘛)))拷呆,否則表示應(yīng)用層沒有處理(如果此消息有QoS機(jī)制,則發(fā)送方在QoS重傳機(jī)制超時
     * 后報出消息發(fā)送失敗的提示)
     * @see #onTransBuffer_C2C_CallBack(Protocal)
     * @since 4.0
     */
    @Override
    public boolean onTransBuffer_C2C_RealTimeSendFaild_CallBack(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發(fā)送沒有成功茬斧,需要上層應(yīng)用作離線處理哦,否則此消息將被丟棄.");
        return false;
    }
}

2梗逮、服務(wù)端主動發(fā)起消息的QoS回調(diào)通知(實現(xiàn)MessageQoSEventListenerS2C類)

public class MessageQoSEventS2CListnerImpl implements MessageQoSEventListenerS2C {
    private static Logger logger = LoggerFactory.getLogger(MessageQoSEventS2CListnerImpl.class);

    @Override
    public void messagesLost(ArrayList<Protocal> lostMessages) {
        logger.debug("【DEBUG_QoS_S2C事件】收到系統(tǒng)的未實時送達(dá)事件通知项秉,當(dāng)前共有"
                + lostMessages.size() + "個包QoS保證機(jī)制結(jié)束,判定為【無法實時送達(dá)】慷彤!");
    }

    @Override
    public void messagesBeReceived(String theFingerPrint) {
        if (theFingerPrint != null) {
            logger.debug("【DEBUG_QoS_S2C事件】收到對方已收到消息事件的通知娄蔼,fp=" + theFingerPrint);
        }
    }
}

3、服務(wù)端配置

public class ServerLauncherImpl extends ServerLauncher {
    // 靜態(tài)類方法:進(jìn)行一些全局配置設(shè)置
    static {
        // 設(shè)置AppKey(此key目前為保留字段底哗,請忽略之)
        ServerLauncher.appKey = "5418023dfd98c579b6001741";

        // 設(shè)置MobileIMSDK服務(wù)端的網(wǎng)絡(luò)監(jiān)聽端口
        ServerLauncherImpl.PORT = 7901;

        // 開/關(guān)Demog日志的輸出
        QoS4SendDaemonS2C.getInstance().setDebugable(true);
        QoS4ReciveDaemonC2S.getInstance().setDebugable(true);
        ServerLauncher.debug = true;

        // TODO 與客戶端協(xié)商一致的心跳敏感模式設(shè)置
//      ServerToolKits.setSenseMode(SenseMode.MODE_10S);

        // 關(guān)閉與Web端的消息互通橋接器(其實SDK中默認(rèn)就是false)
        ServerLauncher.bridgeEnabled = false;
        // TODO 跨服橋接器MQ的URI(本參數(shù)只在ServerLauncher.bridgeEnabled為true時有意義)
//      BridgeProcessor.IMMQ_URI = "amqp://js:19844713@192.168.31.190";
    }

    // 實例構(gòu)造方法
    public ServerLauncherImpl() throws IOException {
        super();
    }

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

4岁诉、服務(wù)端啟動類

溫馨小提示:這里由于小編將服務(wù)端和客戶端集成在同一個項目中,因此如下配置

  1. SpringBoot的CommandLineRunner接口主要用于實現(xiàn)在服務(wù)初始化后跋选,去執(zhí)行一段代碼塊邏輯(run方法)涕癣,這段初始化代碼在整個應(yīng)用生命周期內(nèi)只會執(zhí)行一次!
  2. @Order(value = 1) :按照一定的順序去執(zhí)行前标,value值越小越先執(zhí)行
@Slf4j
@Component
@Order(value = 1)
public class ChatServerRunner implements CommandLineRunner {

    @Override
    public void run(String... strings) throws Exception {
        log.info("================= ↓↓↓↓↓↓ 啟動MobileIMSDK服務(wù)端 ↓↓↓↓↓↓ =================");
        // 實例化后記得startup哦候生,單獨startup()的目的是讓調(diào)用者可以延遲決定何時真正啟動IM服務(wù)
        final ServerLauncherImpl sli = new ServerLauncherImpl();
        // 啟動MobileIMSDK服務(wù)端的Demo
        sli.startup();

        // 加一個鉤子硅确,確保在JVM退出時釋放netty的資源
        Runtime.getRuntime().addShutdownHook(new Thread(sli::shutdown));
    }

}

如果服務(wù)端與客戶端不在同一個項目 柿估,服務(wù)端可直接通過如下方式啟動即可~


在這里插入圖片描述

四足陨、開發(fā)客戶端

在這里插入圖片描述

1零抬、客戶端與IM服務(wù)端連接事件

@Slf4j
public class ChatBaseEventImpl implements ChatBaseEvent {

    @Override
    public void onLoginMessage(int dwErrorCode) {
        if (dwErrorCode == 0) {
            log.debug("IM服務(wù)器登錄/連接成功!");
        } else {
            log.error("IM服務(wù)器登錄/連接失敗嚼松,錯誤代碼:" + dwErrorCode);
        }
    }

    @Override
    public void onLinkCloseMessage(int dwErrorCode) {
        log.error("與IM服務(wù)器的網(wǎng)絡(luò)連接出錯關(guān)閉了坷牛,error:" + dwErrorCode);
    }

}

2、接收消息事件

@Slf4j
public class ChatTransDataEventImpl implements ChatTransDataEvent {

    @Override
    public void onTransBuffer(String fingerPrintOfProtocal, String userid, String dataContent, int typeu) {
        log.debug("[typeu=" + typeu + "]收到來自用戶" + userid + "的消息:" + dataContent);
    }

    @Override
    public void onErrorResponse(int errorCode, String errorMsg) {
        log.debug("收到服務(wù)端錯誤消息痊土,errorCode=" + errorCode + ", errorMsg=" + errorMsg);
    }

}

3犯祠、消息是否送達(dá)事件

@Slf4j
public class MessageQoSEventImpl implements MessageQoSEvent {

    @Override // 對方未成功接收消息的回調(diào)事件 lostMessages:存放消息內(nèi)容
    public void messagesLost(ArrayList<Protocal> lostMessages) {
        log.debug("收到系統(tǒng)的未實時送達(dá)事件通知,當(dāng)前共有" + lostMessages.size() + "個包QoS保證機(jī)制結(jié)束,判定為【無法實時送達(dá)】!");
    }

    @Override // 對方成功接收到消息的回調(diào)事件
    public void messagesBeReceived(String theFingerPrint) {
        if (theFingerPrint != null) {
            log.debug("收到對方已收到消息事件的通知腊凶,fp=" + theFingerPrint);
        }
    }

}

4政鼠、MobileIMSDK初始化配置

public class IMClientManager {
    private static IMClientManager instance = null;

    /**
     * MobileIMSDK是否已被初始化. true表示已初化完成官帘,否則未初始化.
     */
    private boolean init = false;

    public static IMClientManager getInstance() {
        if (instance == null) {
            instance = new IMClientManager();
        }
        return instance;
    }

    private IMClientManager() {
        initMobileIMSDK();
    }

    public void initMobileIMSDK() {
        if (!init) {
            // 設(shè)置AppKey
            ConfigEntity.appKey = "5418023dfd98c579b6001741";

            // 設(shè)置服務(wù)器ip和服務(wù)器端口
            ConfigEntity.serverIP = "127.0.0.1";
            ConfigEntity.serverUDPPort = 7901;

            // MobileIMSDK核心IM框架的敏感度模式設(shè)置
//          ConfigEntity.setSenseMode(SenseMode.MODE_10S);

            // 開啟/關(guān)閉DEBUG信息輸出
            ClientCoreSDK.DEBUG = false;

            // 設(shè)置事件回調(diào)
            ClientCoreSDK.getInstance().setChatBaseEvent(new ChatBaseEventImpl());
            ClientCoreSDK.getInstance().setChatTransDataEvent(new ChatTransDataEventImpl());
            ClientCoreSDK.getInstance().setMessageQoSEvent(new MessageQoSEventImpl());

            init = true;
        }
    }

}

6涌哲、連接IM服務(wù)端初烘,發(fā)送消息

服務(wù)類

public interface IChatService {

    /**
     * 登錄連接IM服務(wù)器請求
     *
     * @param username: 用戶名
     * @param password: 密碼
     * @return: void
     */
    void loginConnect(String username, String password);

    /**
     * 發(fā)送消息
     *
     * @param friendId: 接收消息者id
     * @param msg:      消息內(nèi)容
     * @return: void
     */
    void sendMsg(String friendId, String msg);

}

服務(wù)實現(xiàn)類

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class ChatServiceImpl implements IChatService {

    @Override
    public void loginConnect(String username, String password) {
        // 確保MobileIMSDK被初始化哦(整個APP生生命周期中只需調(diào)用一次哦)
        // 提示:在不退出APP的情況下退出登陸后再重新登陸時局齿,請確保調(diào)用本方法一次谣妻,不然會報code=203錯誤哦减江!
        IMClientManager.getInstance().initMobileIMSDK();

        // * 異步提交登陸名和密碼
        new LocalUDPDataSender.SendLoginDataAsync(username, password) {
            /**
             * 登陸信息發(fā)送完成后將調(diào)用本方法(注意:此處僅是登陸信息發(fā)送完成份企,真正的登陸結(jié)果要在異步回調(diào)中處理哦)。
             * @param code 數(shù)據(jù)發(fā)送返回碼巡莹,0 表示數(shù)據(jù)成功發(fā)出司志,否則是錯誤碼
             */
            protected void fireAfterSendLogin(int code) {
                if (code == 0) {
                    log.debug("數(shù)據(jù)發(fā)送成功!");
                } else {
                    log.error("數(shù)據(jù)發(fā)送失敗降宅。錯誤碼是:" + code);
                }
            }
        }.execute();
    }

    @Override
    public void sendMsg(String friendId, String msg) {
        // 發(fā)送消息(異步提升體驗骂远,你也可直接調(diào)用LocalUDPDataSender.send(..)方法發(fā)送)
        new LocalUDPDataSender.SendCommonDataAsync(msg, friendId) {
            @Override
            protected void onPostExecute(Integer code) {
                if (code == 0) {
                    log.debug("數(shù)據(jù)已成功發(fā)出!");
                } else {
                    log.error("數(shù)據(jù)發(fā)送失敗腰根。錯誤碼是:" + code + "激才!");
                }
            }
        }.execute();
    }

}

五、編寫Controller進(jìn)行測試

@RestController
@RequestMapping("/api")
@Api(tags = "聊天測試-接口")
public class ChatController {

    @Autowired
    private IChatService chatService;

    @PostMapping(value = "/loginConnect", produces = Constants.CONTENT_TYPE)
    @ApiOperation(value = "登陸請求", httpMethod = "POST", response = ApiResult.class)
    public ApiResult loginConnect(@RequestParam String username, @RequestParam String password) {
        chatService.loginConnect(username, password);
        return ApiResult.ok();
    }

    @PostMapping(value = "/sendMsg", produces = Constants.CONTENT_TYPE)
    @ApiOperation(value = "發(fā)送消息", httpMethod = "POST", response = ApiResult.class)
    public ApiResult sendMsg(@RequestParam String friendId, @RequestParam String msg) {
        chatService.sendMsg(friendId, msg);
        return ApiResult.ok();
    }

}

啟動項目唠雕,訪問:http://127.0.0.1:8080/swagger-ui.html

在這里插入圖片描述

1贸营、 loginConnect接口:任意輸入一個賬號密碼登錄連接IM服務(wù)端
在這里插入圖片描述

控制臺日志如下:


在這里插入圖片描述
2、 sendMsg接口:給指定用戶發(fā)送消息岩睁,這里由于只有一個客戶端钞脂,上一步登錄了一個admin賬號,因此小編給admin賬號(也就是自己) 發(fā)送消息
在這里插入圖片描述

控制臺日志如下:


在這里插入圖片描述

六捕儒、總結(jié)

關(guān)于集成可參考MobileIMSDK給出的文檔一步一步實現(xiàn)冰啃,其中給出了通過Java GUI編程實現(xiàn)的一個小demo邓夕,我們可以先將其運行起來,先體驗一下功能阎毅,代碼量也不是太多焚刚,我們可以通過debug方式查看執(zhí)行流程,清楚執(zhí)行流程之后我們就可以將demo中的代碼移植到我們自己的項目中加以修改運用于自己的業(yè)務(wù)中扇调,切勿拿起就跑矿咕,否則一旦運氣不好,將浪費更多的時間去集成狼钮,這樣很不好碳柱!


在這里插入圖片描述

在這里插入圖片描述

案例demo中相關(guān)代碼注釋都有,這里就簡單說下整個流程吧:

  1. 首先啟動IM服務(wù)端
  2. 用戶在客戶端登錄一個用戶與服務(wù)端建立連接保持通信( 客戶端ChatServiceImplloginConnect方法為登錄連接服務(wù)端事件熬芜;服務(wù)端ServerEventListenerImplonUserLoginAction_CallBack方法為服務(wù)端接收的上線通知事件)
  3. 客戶端通過 ChatServiceImplsendMsg方法發(fā)送一條消息莲镣,如果對方在線能接收消息則走服務(wù)端ServerEventListenerImplonTransBuffer_C2C_CallBack方法,否則走onTransBuffer_C2C_RealTimeSendFaild_CallBack方法涎拉;如果對方成功接收到消息瑞侮,客戶端將走MessageQoSEventImplmessagesBeReceived事件,否則走messagesLost事件
  4. 客戶端通過ChatTransDataEventImplonTransBuffer回調(diào)事件接收消息

本文案例demo源碼

https://gitee.com/zhengqingya/java-workspace

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鼓拧,一起剝皮案震驚了整個濱河市半火,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌毁枯,老刑警劉巖慈缔,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異种玛,居然都是意外死亡藐鹤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門赂韵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娱节,“玉大人,你說我怎么就攤上這事祭示∫蘼” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵质涛,是天一觀的道長稠歉。 經(jīng)常有香客問我,道長汇陆,這世上最難降的妖魔是什么怒炸? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮毡代,結(jié)果婚禮上阅羹,老公的妹妹穿的比我還像新娘勺疼。我一直安慰自己,他們只是感情好捏鱼,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布执庐。 她就那樣靜靜地躺著,像睡著了一般导梆。 火紅的嫁衣襯著肌膚如雪轨淌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天问潭,我揣著相機(jī)與錄音猿诸,去河邊找鬼婚被。 笑死狡忙,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的址芯。 我是一名探鬼主播灾茁,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谷炸!你這毒婦竟也來了北专?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤旬陡,失蹤者是張志新(化名)和其女友劉穎拓颓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體描孟,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡驶睦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匿醒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片场航。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖廉羔,靈堂內(nèi)的尸體忽然破棺而出溉痢,到底是詐尸還是另有隱情,我是刑警寧澤憋他,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布孩饼,位于F島的核電站,受9級特大地震影響竹挡,放射性物質(zhì)發(fā)生泄漏镀娶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一此迅、第九天 我趴在偏房一處隱蔽的房頂上張望汽畴。 院中可真熱鬧旧巾,春花似錦、人聲如沸忍些。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罢坝。三九已至廓握,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘁酿,已是汗流浹背隙券。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留闹司,地道東北人娱仔。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像游桩,于是被迫代替她去往敵國和親牲迫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容