因为项目中需要用到实时网页聊天,于是了解到socket.io框架
##1 概述 socket.io封装了websocket等通讯方式,可以实现服务器与浏览器之间的信息推送的功能。
官方网站 提供了一个非常简单的聊天室demo,然而是基于nodejs来做的。为了统一一下,专门去找了一个基于java的socket.io服务端NettySocketIO.
socket.io的原理是基于事件的推送。对于数据的处理就是一个个的事件触发。
比如服务器的某个数据更新了,需要所有浏览器同步更新,就向所有浏览器发送一个更新事件,内容就是更新的数据,或者数据较多的时候告诉客户端自己来取。
这种模型在很多异步服务器中都有使用,在java中,Netty就是一个很典型的事件驱动服务器。于是用netty来做socketio也很顺理成章的样子。至于官方给出的node.js也是以事件驱动而闻名。
##2 服务端
单纯的一个socketio服务器启动起来十分简单:
public void start(){
Configuration configuration = new Configuration();
configuration.setHostname("0.0.0.0");
configuration.setPort(9223);
socketioServer = new SocketIOServer(configuration);
//为指定的事件添加监听
socketioServer.addEventListener("sendMessage", SimpleChatEvent.class ,
new SimpleChatListener(socketioServer));
logger.info("Socket.io 服务已经启动");
socketioServer.start();
}
复制代码
代码看上去很明了。新建一个配置,用配置创建一个服务,添加事件监听并启动。
在NettySocketIO中,事件需要指明一个名称,在这里是"sendMessage",后面是一个java bean,指明了这个事件需要附带的参数。
实现聊天室的关键就在于SimpleChatListener这个类中:
/**
* 简单广播的聊天服务器
* Created by shizhida on 16/5/7.
*/
public class SimpleChatListener implements DataListener<SimpleChatEvent> {
SocketIOServer server;
UserService userService = DubboClient.getClient(UserService.class);
Logger logger = LoggerFactory.getLogger(SimpleChatListener.class);
public SimpleChatListener(SocketIOServer server){
this.server = server;
}
@Override
public void onData(SocketIOClient client, SimpleChatEvent data, AckRequest ackSender) throws Exception {
// broadcast messages to all clients
logger.info("recive message:"+data.getMessage());
String ticket = data.getTicket();
//去其它的服务中获取用户信息
XOResponse response = userService.getMyInfo(ticket);
if(response.getResultStatus() != CommonKeys.response_status_success){
return;
}
Map<String,Object> user = (Map<String, Object>) response.getBody("user");
//拼装返回的参数
Message message = new Message();
message.setMessage(data.getMessage());
message.setTime(new SimpleDateFormat("hh:mm:ss").format(new Date()));
message.setUserIcon((String) user.get("icon"));
message.setUserName((String) user.get("username"));
server.getBroadcastOperations().sendEvent("newMessage", message);
logger.info("send message to client, userName:"+message.getUserName());
}
}
复制代码
在此类中实现DataListener接口,可以见到在onData方法中,收到的data类型就是我们前面指定的SimpleChatEvent。
收到消息后,根据用户的ticket获取用户信息,并组装出返回的消息,通过广播发送到所有的客户端,就会在所有的浏览器都收到这条消息。
在这个demo中,并没有添加诸如聊天记录处理,以及更多其它的花式事件等功能,但是看过代码后就会发现这些功能只是简单的扩展。
##3 浏览器
因为在项目中使用了react,还需要在react中集成socketio的功能。
为了方便调用,我将socketio的实例扔在了组件的state中:
getInitialState: function() {
var ticket = $.cookie("ticket");
var socket = io.connect('http://server.interestpal.com:9223');
socket.on('newMessage', this.reciveMessage);
return {
messages: [],
socket: socket,
ticket: ticket,
content: ""
};
},
复制代码
reciveMessage是此组件中的一个方法。用来在接收到消息时更新state中的messages数组 组件中同样提供一个send方法,用于向服务器发送消息:
reciveMessage : function(data){
var msgs = this.state.messages;
msgs.push(data);
this.setState({
messages:msgs
});
},
send : function(){
if(!$.cookie("ticket")){
alert("用户尚未登录,请登陆后操作")
}
if(this.state.content=="")
return;
this.state.socket.emit("sendMessage",{
ticket:$.cookie("ticket"),
message:this.state.content
});
this.setState({
content:""
});
}
复制代码
剩下的事情就是要根据数据来铺页面了。