一、前言

MobileIMSDK 是什么?

MobileIMSDK  是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。

工程地址是:

本文将实现:

  • 1)基于springboot 集成 MobileIMSDK;
  • 2)开发IM服务端;
  • 3)开发客户端;
  • 4)实现Java客户端与客户端之间的通信。

* 补充说明:本文所示Demo源码,请从文末“本文小结”的最后链接中下载!

二、SpringBoot 集成 MobileIMSDK 准备

2.1 MobileIMSDK下载

MobileIMSDK下载地址:

需要用到的lib包:

  • 1)服务端所需jar包: sdk_binary/Server/
  • 2)客服端所需jar包: sdk_binary/Client_TCP/java/

如下图所示:

2.2 pom.xml中引入相关依赖

由于这里是maven项目,其中一部分jar包可通过maven仓库直接引入,而其余的则通过外部jar包引入方式使用即可~

如下4个需作为外部jar包在pom.xml中引入 :

<!-- [url=https://mvnrepository.com/artifact/com.google.code.gson/gson]https://mvnrepository.com/artifact/com.google.code.gson/gson[/url] -->

<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包也引入进去 -->

<configuration>

<includeSystemScope>true</includeSystemScope>

</configuration>

</plugin>

</plugins>

三、开发服务端

3.1 与客服端的所有数据交互事件(实现ServerEventListener类)

public class ServerEventListenerImpl implements ServerEventListener {

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

/**

* 用户身份验证回调方法定义.

* <p>

* 服务端的应用层可在本方法中实现用户登陆验证。

* <br>

* 注意:本回调在一种特殊情况下——即用户实际未退出登陆但再次发起来登陆包时,本回调是不会被调用的!

* <p>

* 根据MobileIMSDK的算法实现,本方法中用户验证通过(即方法返回值=0时)后

* ,将立即调用回调方法 {@link #onUserLoginAction_CallBack(int, String, IoSession)}。

* 否则会将验证结果(本方法返回值错误码通过客户端的 ChatBaseEvent.onLoginMessage(int dwUserId, int dwErrorCode)

* 方法进行回调)通知客户端)。

*

* @param userId  传递过来的准一id,保证唯一就可以通信,可能是登陆用户名、也可能是任意不重复的id等,具体意义由业务层决定

* @param token   用于身份鉴别和合法性检查的token,它可能是登陆密码,也可能是通过前置单点登陆接口拿到的token等,具体意义由业务层决定

* @param extra   额外信息字符串。本字段目前为保留字段,供上层应用自行放置需要的内容

* @param session 此客户端连接对应的 netty “会话”

* @return 0 表示登陆验证通过,否则可以返回用户自已定义的错误码,错误码值应为:>=1025的整数

*/

@Override

public int onVerifyUserCallBack(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 onUserLoginAction_CallBack(String userId, String extra, Channel session) {

logger.debug("【IM_回调通知OnUserLoginAction_CallBack】用户:"+ userId + " 上线了!");

}

/**

* 用户退出登录回调方法定义(可理解为下线通知回调)。

* <p>

* 服务端的应用层通常可在本方法中实现用户下线通知等。

*

* @param userId  下线的用户user_id

* @param obj

* @param session 此客户端连接对应的 netty “会话”

*/

@Override

public void onUserLogoutAction_CallBack(String userId, Object obj, Channel session) {

logger.debug("【DEBUG_回调通知OnUserLogoutAction_CallBack】用户:"+ userId + " 离线了!");

}

/**

* 通用数据回调方法定义(客户端发给服务端的(即接收user_id="0")).

* <p>

* MobileIMSDK在收到客户端向user_id=0(即接收目标是服务器)的情况下通过

* 本方法的回调通知上层。上层通常可在本方法中实现如:添加好友请求等业务实现。

*

* <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(本方法接收的是发给服务端的消息,所以此参数的值肯定==0)

* @param from_user_id 发送方的user_id

* @param dataContent  数据内容(文本形式)

* @param session      此客户端连接对应的 netty “会话”

* @return true表示本方法已成功处理完成,否则表示未处理成功。此返回值目前框架中并没有特殊意义,仅作保留吧

* @since 4.0

*/

@Override

public boolean onTransBuffer_C2S_CallBack(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();

// 【重要】用户定义的消息或指令协议类型(开发者可据此类型来区分具体的消息或指令)

inttypeu = p.getTypeu();

logger.debug("【DEBUG_回调通知】[typeu="+ typeu + "]收到了客户端"+ from_user_id + "发给服务端的消息:str="+ dataContent);

returntrue;

}

/**

* 通道数据回调函数定义(客户端发给客户端的(即接收方user_id不为“0”的情况)).

* <p>

* <b>注意:</b>本方法当且仅当在数据被服务端成功在线发送出去后被回调调用.

* <p>

* 上层通常可在本方法中实现用户聊天信息的收集,以便后期监控分析用户的行为等^_^。

* <p>

* 提示:如果开启消息QoS保证,因重传机制,本回调中的消息理论上有重复的可能,请以参数 #fingerPrint

* 作为消息的唯一标识ID进行去重处理。

*

* <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(本方法接收的是客户端发给客户端的,所以此参数的值肯定>0)

* @param from_user_id 发送方的user_id

* @param dataContent

* @since 4.0

*/

@Override

public void onTransBuffer_C2C_CallBack(Protocal p) {

// 接收者uid

String userId = p.getTo();

// 发送者uid

String from_user_id = p.getFrom();

// 消息或指令内容

String dataContent = p.getDataContent();

// 消息或指令指纹码(即唯一ID)

String fingerPrint = p.getFp();

// 【重要】用户定义的消息或指令协议类型(开发者可据此类型来区分具体的消息或指令)

inttypeu = p.getTypeu();

logger.debug("【DEBUG_回调通知】[typeu="+ typeu + "]收到了客户端"+ from_user_id + "发给客户端"+ userId + "的消息:str="+ dataContent);

}

/**

* 通用数据实时发送失败后的回调函数定义(客户端发给客户端的(即接收方user_id不为“0”的情况)).

* <p>

* 注意:本方法当且仅当在数据被服务端<u>在线发送</u>失败后被回调调用.

* <p>

* <b>此方法存的意义何在?</b><br>

* 发生此种情况的场景可能是:对方确实不在线(那么此方法里就可以作为离线消息处理了)、

* 或者在发送时判断对方是在线的但服务端在发送时却没有成功(这种情况就可能是通信错误

* 或对方非正常通出但尚未到达会话超时时限)。<br><u>应用层在此方法里实现离线消息的处理即可!</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(本方法接收的是客户端发给客户端的,所以此参数的值肯定>0),此id在本方法中不一定保证有意义

* @param from_user_id 发送方的user_id

* @param dataContent  消息内容

* @param fingerPrint  该消息对应的指纹(如果该消息有QoS保证机制的话),用于在QoS重要机制下服务端离线存储时防止重复存储哦

* @return true表示应用层已经处理了离线消息(如果该消息有QoS机制,则服务端将代为发送一条伪应答包

* (伪应答仅意味着不是接收方的实时应答,而只是存储到离线DB中,但在发送方看来也算是被对方收到,只是延

* 迟收到而已(离线消息嘛))),否则表示应用层没有处理(如果此消息有QoS机制,则发送方在QoS重传机制超时

* 后报出消息发送失败的提示)

* @see #onTransBuffer_C2C_CallBack(Protocal)

* @since 4.0

*/

@Override

public boolean onTransBuffer_C2C_RealTimeSendFaild_CallBack(Protocal p) {

// 接收者uid

String userId = p.getTo();

// 发送者uid

String from_user_id = p.getFrom();

// 消息或指令内容

String dataContent = p.getDataContent();

// 消息或指令指纹码(即唯一ID)

String fingerPrint = p.getFp();

// 【重要】用户定义的消息或指令协议类型(开发者可据此类型来区分具体的消息或指令)

inttypeu = p.getTypeu();

logger.debug("【DEBUG_回调通知】[typeu="+ typeu + "]客户端"+ from_user_id + "发给客户端"+ userId + "的消息:str="+ dataContent

+ ",因实时发送没有成功,需要上层应用作离线处理哦,否则此消息将被丢弃.");

returnfalse;

}

}

3.2 服务端主动发起消息的QoS回调通知(实现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事件】收到系统的未实时送达事件通知,当前共有"

+ lostMessages.size() + "个包QoS保证机制结束,判定为【无法实时送达】!");

}

@Override

public void messagesBeReceived(String theFingerPrint) {

if(theFingerPrint != null) {

logger.debug("【DEBUG_QoS_S2C事件】收到对方已收到消息事件的通知,fp="+ theFingerPrint);

}

}

}

3.3 服务端配置

public class ServerLauncherImpl extends ServerLauncher {

// 静态类方法:进行一些全局配置设置

static{

// 设置MobileIMSDK服务端的网络监听端口

ServerLauncherImpl.PORT = 7901;

// 开/关Demog日志的输出

QoS4SendDaemonS2C.getInstance().setDebugable(true);

QoS4ReciveDaemonC2S.getInstance().setDebugable(true);

ServerLauncher.debug = true;

// TODO 与客户端协商一致的心跳敏感模式设置

//      ServerToolKits.setSenseMode(SenseMode.MODE_10S);

// 关闭与Web端的消息互通桥接器(其实SDK中默认就是false)

ServerLauncher.bridgeEnabled = false;

// TODO 跨服桥接器MQ的URI(本参数只在ServerLauncher.bridgeEnabled为true时有意义)

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

}

// 实例构造方法

public ServerLauncherImpl() throws IOException {

super();

}

/**

* 初始化消息处理事件监听者.

*/

@Override

protected void initListeners() {

// ** 设置各种回调事件处理实现类

this.setServerEventListener(newServerEventListenerImpl());

this.setServerMessageQoSEventListener(newMessageQoSEventS2CListnerImpl());

}

}

3.4 服务端启动类

温馨小提示:这里由于小编将服务端和客户端集成在同一个项目中,因此如下配置:

  • SpringBoot的CommandLineRunner接口主要用于实现在服务初始化后,去执行一段代码块逻辑(run方法),这段初始化代码在整个应用生命周期内只会执行一次!
  • @Order(value = 1) :按照一定的顺序去执行,value值越小越先执行

@Slf4j

@Component

@Order(value = 1)

public class ChatServerRunner implements CommandLineRunner {

@Override

public void run(String... strings) throws Exception {

log.info("================= ↓↓↓↓↓↓ 启动MobileIMSDK服务端 ↓↓↓↓↓↓ =================");

// 实例化后记得startup哦,单独startup()的目的是让调用者可以延迟决定何时真正启动IM服务

final ServerLauncherImpl sli = new ServerLauncherImpl();

// 启动MobileIMSDK服务端的Demo

sli.startup();

// 加一个钩子,确保在JVM退出时释放netty的资源

Runtime.getRuntime().addShutdownHook(newThread(sli::shutdown));

}

}

如果服务端与客户端不在同一个项目 ,服务端可直接通过如下方式启动即可~

四、开发客户端

4.1 客户端与IM服务端连接事件

@Slf4j

public class ChatBaseEventImpl implements ChatBaseEvent {

@Override

public void onLoginMessage(int dwErrorCode) {

if(dwErrorCode == 0) {

log.debug("IM服务器登录/连接成功!");

} else{

log.error("IM服务器登录/连接失败,错误代码:"+ dwErrorCode);

}

}

@Override

public void onLinkCloseMessage(int dwErrorCode) {

log.error("与IM服务器的网络连接出错关闭了,error:"+ dwErrorCode);

}

}

4.2 接收消息事件

@Slf4j

public class ChatTransDataEventImpl implements ChatTransDataEvent {

@Override

public void onTransBuffer(String fingerPrintOfProtocal, String userid, String dataContent, inttypeu) {

log.debug("[typeu="+ typeu + "]收到来自用户"+ userid + "的消息:"+ dataContent);

}

@Override

public void onErrorResponse(int errorCode, String errorMsg) {

log.debug("收到服务端错误消息,errorCode="+ errorCode + ", errorMsg="+ errorMsg);

}

}

4.3 消息是否送达事件

@Slf4j

public class MessageQoSEventImpl implements MessageQoSEvent {

@Override// 对方未成功接收消息的回调事件 lostMessages:存放消息内容

public void messagesLost(ArrayList<Protocal> lostMessages) {

log.debug("收到系统的未实时送达事件通知,当前共有"+ lostMessages.size() + "个包QoS保证机制结束,判定为【无法实时送达】!");

}

@Override// 对方成功接收到消息的回调事件

public void messagesBeReceived(String theFingerPrint) {

if(theFingerPrint != null) {

log.debug("收到对方已收到消息事件的通知,fp="+ theFingerPrint);

}

}

}

4.4 MobileIMSDK初始化配置

public class IMClientManager {

private static IMClientManager instance = null;

/**

* MobileIMSDK是否已被初始化. true表示已初化完成,否则未初始化.

*/

privatebooleaninit = false;

public static IMClientManager getInstance() {

if(instance == null) {

instance = new IMClientManager();

}

return instance;

}

private IMClientManager() {

initMobileIMSDK();

}

public void initMobileIMSDK() {

if(!init) {

// 设置服务器ip和服务器端口

ConfigEntity.serverIP = "127.0.0.1";

ConfigEntity.serverPort = 8901;

// MobileIMSDK核心IM框架的敏感度模式设置

//           ConfigEntity.setSenseMode(SenseMode.MODE_10S);

// 开启/关闭DEBUG信息输出

ClientCoreSDK.DEBUG = false;

// 设置事件回调

ClientCoreSDK.getInstance().setChatBaseEvent(newChatBaseEventImpl());

ClientCoreSDK.getInstance().setChatTransDataEvent(newChatTransDataEventImpl());

ClientCoreSDK.getInstance().setMessageQoSEvent(newMessageQoSEventImpl());

init = true;

}

}

}

4.5 连接IM服务端,发送消息

服务类:

public interface IChatService {

/**

* 登录连接IM服务器请求

*

* @param username: 用户名

* @param password: 密码

* @return: void

*/

void loginConnect(String username, String password);

/**

* 发送消息

*

* @param friendId: 接收消息者id

* @param msg:      消息内容

* @return: void

*/

void sendMsg(String friendId, String msg);

}

服务实现类:

@Slf4j

@Service

@Transactional(rollbackFor = Exception.class)

public class ChatServiceImpl implements IChatService {

@Override

public void loginConnect(String username, String password) {

// 确保MobileIMSDK被初始化哦(整个APP生生命周期中只需调用一次哦)

// 提示:在不退出APP的情况下退出登陆后再重新登陆时,请确保调用本方法一次,不然会报code=203错误哦!

IMClientManager.getInstance().initMobileIMSDK();

// * 异步提交登陆名和密码

new LocalUDPDataSender.SendLoginDataAsync(username, password) {

/**

* 登陆信息发送完成后将调用本方法(注意:此处仅是登陆信息发送完成,真正的登陆结果要在异步回调中处理哦)。

* @param code 数据发送返回码,0 表示数据成功发出,否则是错误码

*/

protected void fireAfterSendLogin(int code) {

if(code == 0) {

log.debug("数据发送成功!");

} else{

log.error("数据发送失败。错误码是:"+ code);

}

}

}.execute();

}

@Override

public void sendMsg(String friendId, String msg) {

// 发送消息(异步提升体验,你也可直接调用LocalUDPDataSender.send(..)方法发送)

new LocalUDPDataSender.SendCommonDataAsync(msg, friendId) {

@Override

protected void onPostExecute(Integer code) {

if(code == 0) {

log.debug("数据已成功发出!");

} else{

log.error("数据发送失败。错误码是:"+ code + "!");

}

}

}.execute();

}

}

五、编写Controller进行测试

@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(@RequestParamString username, @RequestParamString password) {

chatService.loginConnect(username, password);

return ApiResult.ok();

}

@PostMapping(value = "/sendMsg", produces = Constants.CONTENT_TYPE)

@ApiOperation(value = "发送消息", 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服务端:

控制台日志如下:

2)sendMsg接口:

给指定用户发送消息:这里由于只有一个客户端,上一步登录了一个admin账号,因此小编给admin账号(也就是自己) 发送消息

控制台日志如下:

六、本文小结

关于集成可参考MobileIMSDK给出的文档一步一步实现。

该开源工程对应的官方文档比较齐全,需要哪个端,就去看对应端的手册就好了。

1)Demo安装和使用

2)开发者指南

3)API文档

另外:作者给出了通过Java GUI编程实现的一个小demo,我们可以先将其运行起来,先体验一下功能,代码量也不是太多,我们可以通过debug方式查看执行流程。

清楚执行流程之后我们就可以将demo中的代码移植到我们自己的项目中加以修改运用于自己的业务中,切勿拿起就跑,否则一旦运气不好,将浪费更多的时间去集成,这样很不好!

最后:案例demo中相关代码注释都有,这里就简单说下整个流程吧:

  • 1)首先启动IM服务端
  • 2)用户在客户端登录一个用户与服务端建立连接保持通信( 客户端ChatServiceImpl中loginConnect方法为登录连接服务端事件;服务端ServerEventListenerImpl中onUserLoginVerify方法为服务端接收的上线通知事件);
  • 3)客户端通过 ChatServiceImpl中sendMsg方法发送一条消息,如果对方在线能接收消息则走服务端ServerEventListenerImpl中onTransferMessage4C2C方法,否则走onTransferMessage_RealTimeSendFaild方法;如果对方成功接收到消息,客户端将走MessageQoSEventImpl中messagesBeReceived事件,否则走messagesLost事件;
  • 4)客户端通过ChatMessageEvent中onRecieveMessage回调事件接收消息。

附:本文案例demo源码下载:

附录:更多IM聊天新手实践代码

  1. 跟着源码学IM(一):手把手教你用Netty实现心跳机制、断线重连机制
  2. 跟着源码学IM(二):自已开发IM很难?手把手教你撸一个Andriod版IM
  3. 跟着源码学IM(三):基于Netty,从零开发一个IM服务端
  4. 跟着源码学IM(四):拿起键盘就是干,教你徒手开发一套分布式IM系统
  5. 跟着源码学IM(五):正确理解IM长连接、心跳及重连机制,并动手实现
  6. 跟着源码学IM(六):手把手教你用Go快速搭建高性能、可扩展的IM系统
  7. 跟着源码学IM(七):手把手教你用WebSocket打造Web端IM聊天
  8. 跟着源码学IM(八):万字长文,手把手教你用Netty打造IM聊天
  9. 跟着源码学IM(九):基于Netty实现一套分布式IM系统
  10. 跟着源码学IM(十):基于Netty,搭建高性能IM集群(含技术思路+源码)

SpringBoot集成开源IM框架MobileIMSDK,实现即时通讯IM聊天功能的更多相关文章

  1. SpringBoot学习笔记(五):SpringBoot集成lombok工具、SpringBoot集成Shiro安全框架

    SpringBoot集成lombok工具 什么是lombok? 自动生成setget方法,构造函数,打印日志 官网:http://projectlombok.org/features/index. 平 ...

  2. layim即时通讯实例各功能整合

    一.系统演示1.1 聊天窗体主界面演示 1.2 模拟两人在线聊天(点击图片查看演示视频) 1.3 在线演示> 在线演示,点击进入系统到这里,若是您想要的,接下来听我娓娓道来二.开发工具开发软件: ...

  3. SpringBoot集成Shiro安全框架

    跟着我的步骤:先运行起来再说 Spring集成Shiro的GitHub:https://github.com/yueshutong/shiro-imooc 一:导包 <!-- Shiro安全框架 ...

  4. 基于Facebook开源框架SocketRocket的即时通讯

    SocketRocket 介绍: SocketRock 是 Facebook 开源的框架,基于 WebSocket 客户端类库,适用于 iOS.Mac OS.tv OS.GitHub 传送门:http ...

  5. java SSM 框架 代码生成器 websocket即时通讯 shiro redis

    1.   权限管理:点开二级菜单进入三级菜单显示 角色(基础权限)和按钮权限      角色(基础权限): 分角色组和角色,独立分配菜单权限和增删改查权限.      按钮权限: 给角色分配按钮权限. ...

  6. ENTBOOST 2014.180L 发布,开源企业IM免费企业即时通讯

    ENTBOOST,VERSION 2014.180 Linux版本发布,主要增加企业IM应用集成功能,完善安卓SDK功能及部分BUG修正: 7/1(明天)发布Windows版本,敬请关注! ENTBO ...

  7. 开源企业IM-免费企业即时通讯-ENTBOOST V2014.180 Linux版本号正式公布

    ENTBOOST,VERSION 2014.180 Linux版本号公布,主要添加企业IM应用集成功能,完好安卓SDK功能及部分BUG修正: 7/1(明天)公布Windows版本号,敬请关注! ENT ...

  8. 开源企业IM-免费企业即时通讯-ENTBOOST V2014.180 Windows版本号正式公布

    ENTBOOST,VERSION 2014.180 Linux版本号公布,主要添加企业IM应用集成功能,完好安卓SDK功能及部分BUG修正. 下一版本号公布时间.7月15日.敬请关注. ENTBOOS ...

  9. 开源企业IM-免费企业即时通讯-ENTBOOST V0.9版本号公布

    ENTBOOST V0.9版本号公布,更新内容:1.完好多人群组聊天,提高群组聊天性能及稳定性:2.苹果IOS SDK.添加联系人管理功能,优化API和内部流程.修复部分BUG.3.添加企业应用功能集 ...

  10. 开源企业IM-免费企业即时通讯-ENTBOOST V2014.183 Windows版本号正式宣布

    ENTBOOST,VERSION 2014.183 Windows(点击下载)版本号公布.主要添加PC端P2P(点对点)文件传输功能:公布安卓Android手机clientAPP 1.0版本号.公布苹 ...

随机推荐

  1. 快速理解和使用stream流

    数据量越大,硬件内核数越多,stream流相比传统for循环速度越快. 原因是stream流是可以并行处理的. 如果要使用stream流,可以直接用flatmap把外层嵌套扁平化,只留下自己需要处理的 ...

  2. linux 基础(4)文件结构标准 FHS

    使用 linux 进行各种各样开发的人实在太多了,因此一套统一的"什么文件存放在哪里"的规范应运而生,被称为 FHS(Filesystem Hierarchy Standard). ...

  3. mysql临时启动不了的问题处理。

    getenforce SELinux状态 setenforce 0 临时关闭SELinux 然后启动mysql就能成功

  4. 二元一次不定方程(Exgcd)(更方便的解法)

    扩展欧几里得算法(Exgcd) 裴蜀定理 对于任意一组整数 \(a,b\),存在一组整数 \(x,y\),满足 \(ax+by=\gcd(a,b)\). Proof: 考虑数学归纳法. 当 \(b=0 ...

  5. 全链路追踪 & 性能监控工具 SkyWalking 实战

    Skywalking介绍 Skywalking是一个国产的开源框架,2015年有吴晟个人开源,2017年加入Apache孵化器,国人开源的产品,主要开发人员来自于华为,2019年4月17日Apache ...

  6. jenkins集成cucumber-resport报告

    需要安装的jenkins插件-Cucumber Reports jenkins版本:2.273 jenkins插件下载地址:点击下载 下载插件后通过jenkins插件管理上传已下载好的插件即可 等待j ...

  7. TSCTF-J2024 密码向WP(5/8)

    ezRSA part 1 #part1 p = getPrime(512) q = getPrime(512) n = p * q phi = (p-1) * (q-1) d = getPrime(2 ...

  8. 配置NVIDIA Container Runtime和容器运行GPUStack教程

    GPUStack 是一个设计用于运行大模型的开源 GPU 集群管理器,提供私有部署的大模型服务,支持大语言模型.Embedding 文本嵌入模型.Reranker 重排序模型.Vision 多模态模型 ...

  9. MySQL创新版9.1.0于2024年10月15日正式发布-新功能特性先睹为快[译]

    这个十月发布的版本是MySQL宣布转向新的LTS/Innovation发布模式后的第三个长期支持(LTS)版本和第二个创新(Innovation)版本.更多细节请参见MySQL发布:创新和长期支持.在 ...

  10. Spring源码学习 ------ IoC——AOP

    一直想抽空把Spring源码拿来读读,但真正去做这件事的时候发现不简单,Spring发展这么多年,它的规模已不是一个一般的开源框架所能比的,它的主要架构和流程不是非常清晰,很难抓到要害,但有一点可以肯 ...