简介:全平台开源即时通讯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步:准备回调通知的实现类
① 框架基本事件回调实现类:
复制代码
/**
@since 3.1
*/
public class ServerEventListenerImpl implements ServerEventListener
{
private static Logger logger = LoggerFactory.getLogger(ServerEventListenerImpl.class);
/**
<p>
<p>
@return 0 表示登陆验证通过,否则可以返回用户自已定义的错误码,错误码值应为:>=1025的整数
*/
@Override
public int onUserLoginVerify(String userId, String token, String extra, Channel session)
{
logger.debug(“【DEBUG_回调通知】正在调用回调方法:OnVerifyUserCallBack…(extra=”+extra+”)”);
return 0;
}
/**
<p>
@param session 此客户端连接对应的 netty “会话”
*/
@Override
public void onUserLoginSucess(String userId, String extra, Channel session)
{
logger.debug(“【IM_回调通知onUserLoginSucess】用户:”+userId+” 上线了!”);
}
/**
<p>
@see {@link OnlineProcessor#setBeKickoutCodeForChannel(Channel, int)}
*/
@Override
public void onUserLogout(String userId, Channel session, int beKickoutCode)
{
logger.debug(“【DEBUG_回调通知onUserLogout】用户:”+userId+” 离线了(beKickoutCode=”+beKickoutCode+”)!”);
}
/**
<p>
<p>
@since 6.2
*/
@Override
public boolean onTransferMessage4C2CBefore(Protocal p, Channel session)
{
return true;
}
/**
<p>
<p>
@since 6.2
*/
@Override
public boolean onTransferMessage4C2SBefore(Protocal p, Channel session)
{
return true;
}
/**
<p>
<p>
@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;
}
/**
<p>
<p>
<p>
@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>
<p>
<pre style="border: 1px solid #eaeaea;background-color: #fff6ea;border-radius: 6px;">
<p>
<p>
<p>
@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回调通知实现类:
复制代码
/**
<p>
<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>
<p>
@see MessageQoSEventListenerS2C
*/
public class MessageQoSEventS2CListnerImpl implements MessageQoSEventListenerS2C
{
private static Logger logger = LoggerFactory.getLogger(MessageQoSEventS2CListnerImpl.class);
/**
可通过指纹特征码找到原消息并可以UI上将其标记为”发送失败“以便即时告之用户
*/
@Override
public void messagesLost(ArrayList<Protocal> lostMessages)
{
logger.debug(“【DEBUG_QoS_S2C事件】收到系统的未实时送达事件通知,当前共有”
+lostMessages.size()+"个包QoS保证机制结束,判定为【无法实时送达】!");
}
/**
<p>
logger.debug("【DEBUG_QoS_S2C事件】收到对方已收到消息事件的通知,fp="+theFingerPrint);
}<p>
@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());
}
/**
@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;
}
/**
@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能双击就运行吧?)
正如“什么是核心库工程?”一节所说,MobileIMSDK的核心库是不能直接运行的,它需要打成jar包被您的工程引用并调用后,才能发挥它的作用,所以MobileIMSDK的Demo演示工程的目的就是为了告诉你:如何引用MobileIMSDK的核心库jar包、如何调用MobileIMSDK的API,读Demo代码就知道如何使用它了(所以Demo代码唯一的意义就是为您演示库的调用,别无他用)!
为了方便日后的升级,建议使用MobileIMSDK编译好的核心库jar包,当然您也可以直接把MobileIMSDK核心库源库放到您的工程中(而不是使用编译好的jar包)。

1 您需要准备一个SSL/TLS证书(支持自签名证书)
可以使用正规CA机构签发的证书,也可以使用自签名证书,如何生成自签名证书可自行百度,这方面资料很丰富。
证书文件就像这样:

注:如果你不想买证书,也不知道如何生成自签名证书,可以跟着这篇文章自已做《手把手教你为基于Netty的IM生成自签名SSL/TLS证书》。
2 代码中启用SSL/TLS加密




(▲ 服务端启动时控制台下关于已开启TLS/SSL加密的log输出(run.bat运行时))

(▲ 服务端启动时控制台下关于已开启TLS/SSL加密的log输出(IDEA中运行时))

(▲ 客户端发起WebSocket连接时,服务端控制台下带有TLS/SSL信息的握手log)