java socket.io实现即时通讯_个推+nettysocketio实现IM即时通讯

目标

技术选型

前端

后端java

MAVEN配置

离线消息

相关材料

目标

实现WEB和APP通用的即时通讯功能。一份代码同时支持各种WEB浏览器以及IOS,Android环境下的APP

技术选型

使用基于netty的nettysocketio实现,后台为JS,前台为HTML5页面,部署时采用nginx中转,java部署在tomcat上

前端

var socket

if(/^plus|android/.test(this.agent)) {

//android的默认浏览器内核不支持websocket10草案

socket = socketIO.connect(LContext.imUrl, {

transports: ['polling']

})

} else {

socket = socketIO.connect(LContext.imUrl)

}

socket.on('reconnect_attempt', function() {

socket.io.opts.transports = ['polling']

})

socket.on('connect', function() {

var data = lpage.user.getData()

console.log('连接成功..')

console.log('Client has connected to the server!\n' + JSON.stringify(data))

//登记当前会话关联的用户

socket.emit('startConnection', {

extra: {

id: data.id,

token: data.token

}

})

})

socket.on('startConnection', function() {

console.log('连接到服务器')

lpage.socket = socket

// document.querySelector('header .mui-title').innerText = lpage.fromUserName

})

//注册事件,服务器通过事件调用本方法

socket.on('push', function(data, ackCallback) {

console.log('接收消息' + JSON.stringify(data))

lpage.addNews(data)

if(ackCallback) {

console.log('接收到消息后,返回消息给服务器确认')

ackCallback('返回到服务器的确认消息')

}

})

socket.on('disconnect', function() {

console.log('断开连接')

lpage.socket = undefined

setTimeout(function() {

lpage.initConnection()

}, 5000)

//5秒后尝试恢复连接

// document.querySelector('header .mui-title').innerText = '未连接(点击重新连接)'

})

// socket.on('error', (error) => {

// console.log('error' + JSON.stringify(error))

// })

//

// socket.on('connect_error', (error) => {

// console.log('error' + JSON.stringify(error))

// })

//

// socket.on('connect_timeout', (error) => {

// console.log('error' + JSON.stringify(error))

// })

awk

后端java

@Service

public class IMHandleService implements ApplicationListener {

Logger logger = Logger.getLogger(IMHandleService.class);

@Override

public void onApplicationEvent(ContextRefreshedEvent event) {

logger.info("运行init event on IMHandleService");

if (event.getApplicationContext().getParent() != null)// root

// application

// context

// 没有parent,他就是老大.

{

// 需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。

logger.info("\n\n\n\n\n__\n\n\n已经加载了ContextRefreshedEvent\n\n\n\n");

return;

}

// 或者下面这种方式

// if (event.getApplicationContext().getDisplayName().equals("Root

// WebApplicationContext")) {

// System.out.println("\n\n\n\n\n加载一次的 \n\n ____\n\n\n\n");

//

// logger.info("\n\n\n\n\n__\n\n\n加载一次的

// \n\n_____\n\n");

// }

Configuration config = new Configuration();

config.setHostname("localhost");

config.setPort(10086);

config.setUpgradeTimeout(60000000);// 设置websocket过期时间

// config.setFirstDataTimeout(60000000);

// config.setPingInterval(28);

SocketIOServer server = new SocketIOServer(config);

logger.info(server.getConfiguration().getFirstDataTimeout() + " " + server.getConfiguration().getPingInterval()

" " + server.getConfiguration().getUpgradeTimeout() + " "

server.getConfiguration().getPingTimeout());

server.addConnectListener(new ConnectListener() {// 添加客户端连接监听器

@Override

public void onConnect(SocketIOClient client) {

logger.info(client.getRemoteAddress().toString() + " " + client.getTransport().getValue() + " "

client.getHandshakeData().getUrl() + " 接入" + client.getSessionId());

// 调用客户端的事件

client.sendEvent("startConnection", "hello");

}

});

server.addDisconnectListener(new DisconnectListener() {

@Override

public void onDisconnect(SocketIOClient client) {

logger.info(client.getRemoteAddress().toString() + " " + client.getTransport().getValue() + " "

client.getHandshakeData().getUrl() + " 断开" + client.getSessionId());

// 清理连接的session

// 检测登录状态,已登录

// 保存当前会话

String userId = sessionUserMap.get("sessionUserMap", client.getSessionId().toString());

if (userId == null) {

return;

}

// 清除在线状态

sessionUserMap.delete("sessionUserMap", client.getSessionId().toString());

userChatSession.remove(SESSION_STORAGE_PREFIX + userId, client.getSessionId().toString());

logger.info("清理session userId= " + userId);

logger.info("session列表长度 " + userChatSession.members(SESSION_STORAGE_PREFIX + userId).toArray().length);

}

});

/**

监听事件,客户端连接成功后调用本方法

*/

server.addEventListener("startConnection", Operation.class, new DataListener() {

@Override

public void onData(final SocketIOClient client, Operation data, AckRequest ackRequest) {

if (data.extra == null || data.extra.id == null || data.extra.token == null

|| !tokenManager.checkToken(new TokenModel(data.extra.id.toString(), data.extra.token))) {

return;

}

// 检测登录状态,已登录

// 保存当前会话

sessionUserMap.put("sessionUserMap", client.getSessionId().toString(), data.extra.id.toString());

userChatSession.add(SESSION_STORAGE_PREFIX + data.extra.id.toString(),

client.getSessionId().toString());

logger.info("确认连接成功,绑定userId= " + data.extra.id.toString() + " sessionId "

client.getSessionId().toString());

logger.info("session列表长度 "

userChatSession.members(SESSION_STORAGE_PREFIX + data.extra.id.toString()).toArray().length);

// 握手

// if (data.getMessage().equals("hello")) {

// Long userid = data.getUser();

// logger.info(Thread.currentThread().getName() +

// "web读取到的userid:" + userid);

// // send message back to client with ack callback

// // WITH data

// client.sendEvent("push", new

// AckCallback(String.class) {

// @Override

// public void onSuccess(String result) {

// logger.info("ack from client: " + client.getSessionId() + "

// data: " + result);

// }

// }, context.SESSION_TIME);

//

// } else {

// logger.info("行情接收到了不应该有的web客户端请求1111...");

// }

}

});

/**

监听事件,客户端通过事件调用本方法

*/

server.addEventListener("message", Operation.class, new DataListener() {

@Override

public void onData(final SocketIOClient client, Operation data, AckRequest ackRequest) {

// 握手

if (data.getMessage().equals("hello")) {

Long userid = data.getTargetuser();

logger.info(Thread.currentThread().getName() + "web读取到的userid:" + userid);

// send message back to client with ack callback

// WITH data

client.sendEvent("push", new AckCallback(String.class) {

@Override

public void onSuccess(String result) {

logger.info("接收到客户端反馈 " + client.getSessionId() + " data: " + result);

}

});

} else {

logger.info("接收到了不应该有的web客户端请求...");

}

}

});

if (serverBuff == null) {

logger.info("初始化 IMHandleService的 监听server");

try {

server.start();

serverBuff = server;

} catch (Exception e) {

e.printStackTrace();

logger.error("\n\nIM服务器启动失败...\n\n");

}

// try {

// Thread.sleep(Integer.MAX_VALUE);

// } catch (InterruptedException e) {

// // TODO Auto-generated catch block

// e.printStackTrace();

// }

// server.stop();

} else {

logger.info(" IMHandleService的 监听server已启动");

}

}

@PreDestroy

public void closeSocket() {

logger.info(" IMHandleService 关闭中...");

if (serverBuff != null) {

serverBuff.stop();

for (String sessionId : sessionUserMap.keys("sessionUserMap")) {

String userId = sessionUserMap.get("sessionUserMap", sessionId);

userChatSession.remove(SESSION_STORAGE_PREFIX + userId, sessionId);

sessionUserMap.delete("sessionUserMap", sessionId);

logger.info("清理session " + userId + " 当前长度为" + sessionUserMap.keys("sessionUserMap").size() + " "

userChatSession.size(SESSION_STORAGE_PREFIX + userId));

}

logger.info(" IMHandleService 已关闭完成");

}

}

}

http

MAVEN配置

com.corundumstudio.socketio

netty-socketio

1.7.12

xml

离线消息

对于所有消息,默认都通过在线服务发送,当发送失败时,启用个推的JAVA SDK进行消息推送即可,具体参考个推官网

在WEB和IOS设备上都可以正常使用,但是在androidAPP中会出现一分钟左右就断线一次的问题。最后在一篇博客上找到了问题所在,由于android的默认浏览器内核支持的websocket版本为10,而Netty的默认实现目前还只支持到7.6,如果有兴趣自己升级的话,可以参考netty升级websocket草案10,

本文链接:http://blog.betweenfriends.cn/post/imnettysocketio.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值