由于项目中需要增加一个内聊工具,以前用过 comet 实现过一些消息推送的东西,总感觉不是最合理的方式。是于我搜索发现spring也有websocket这个东西,发现就是我想要的。
经过折腾算是可以实现可以点对点通讯,或点对多的通讯,也可以跟spring项目整合在一起。 为了后续备查,就把这个草稿就记录下:
首先,引入websocket的jar包:
创建两个类功能实现类:
1.HandshakeInterceptor.java
package com.ihome.websocket;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import java.util.Map;
public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
super.afterHandshake(request, response, wsHandler, ex);
}
}
2.WebsocketEndPoint.java
package com.ihome.websocket;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.alibaba.fastjson.JSON;
import com.ihome.service.UserService;
/**
* 用户通讯聊天控制器类
*
* @author yyp
* 2014年12月27日下午6:56:46
*/
@RequestMapping(value="/chat")
@Controller
public class WebsocketEndPoint extends TextWebSocketHandler {
protected Map<String, WebSocketSession> map = new HashMap<>(); // 在线人员登记
protected Map<String, Object> userMap = null; //当前Ws会话用户Map
protected String fromUser = null; //消息发送方用户
protected String toUsers = null; // 消息接收方用户
protected String msgModel = null; // 消息模式
protected String content = null; //消息内容
protected Integer layer = null; //消息层,主要给前台用的,也可以不带个参数
protected String type = null; //消息类型,如单聊,群聊
protected final String OFFLINE = "offline";
protected final String ONLINE = "online";
protected final String CHAT = "chat";
protected Set<WebSocketSession> sendSessions = null; //消息发送session set
protected List<Map<String, Object>> users = null; //用户信息 list
@Autowired
private UserService userService;
@Override
protected void handleTextMessage(WebSocketSession session,TextMessage message) throws Exception {
super.handleTextMessage(session, message);
this.fromUser = getSessionUser(session);
WsChat wsChat = null;
if(userMap == null){
wsChat = new WsChat();
wsChat.setContent("请登录..");
wsChat.setFrom(this.fromUser);
session.sendMessage(new TextMessage(JSON.toJSONBytes(wsChat)));
return;
}
String[] payloadArr = message.getPayload().split(",");
if(payloadArr != null && payloadArr.length > 0){
int len = payloadArr.length;
for (int i = 0; i < len; i++) {
String text = payloadArr[i].replace("\"", "").replace("{", "").replace("}", "");
String[] textArr = text.split(":");
if(textArr != null){
String key = textArr[0];
String value = textArr.length == 2 ? textArr[1] : "";
if(key.equals("msgModel")){
this.msgModel = value;
}else if(key.equals("content")){
content = value;
}else if(key.equals("layer")){
layer = Integer.parseInt(value);
}else if(key.equals("type")){
type = value;
}else if(key.equals("toUsers")){
this.toUsers = value;
}
}
}
}
//不继续处理上线、离线类消息
if(ONLINE.equals(msgModel) || OFFLINE.equals(msgModel)){
return;
}
//初始货消息类
Set<String> uset = null;
if(StringUtils.isNotEmpty(toUsers)){
String[] userArr = toUsers.split(",");
if(userArr != null && userArr.length > 0){
uset = new HashSet<String>();
for (int j = 0; j < userArr.length; j++) {
uset.add(userArr[j]);
}
}
}
//准备发送消息
if(uset != null && uset.size() > 0){
WebSocketSession sendSession = null;
for(String user : uset){
sendSession = map.get(user);
if(sendSession != null){
wsChat = new WsChat();
wsChat.setFrom(fromUser);
wsChat.setContent(content);
wsChat.setLayer(layer);
wsChat.setType(type);
wsChat.setMsgModel(msgModel);
sendSession.sendMessage(new TextMessage(JSON.toJSONString(wsChat)));
}
}
}
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//定义当前消息状态
this.msgModel = ONLINE;
this.userMap = getSessionUserMap(session);
if(userMap != null && userMap.size() > 0){
this.fromUser = (String) userMap.get("code");
this.map.put(fromUser, session); //添加在线用户
String msg = fromUser +" 上线了,当前在线共 "+ map.size() +" 人!";
System.out.println("========>MSG:"+msg);
//向所有在线用户消息
sendWsMessage(msg);
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
this.msgModel = OFFLINE;
System.out.println("closeStatus:"+status);
Map<String, Object> userMap = getSessionUserMap(session);
if(userMap != null && userMap.size() > 0){
this.fromUser = getSessionUser(session);
this.map.remove(this.fromUser);
//向所有在线用户消息
String msg = fromUser +" 离线了,当前在线共 "+ map.size() +" 人!";
System.out.println("========>MSG:"+msg);
sendWsMessage(msg);
}
}
//~ 自定义的方法 ---------------------
/**
* 发送聊天的消息
* @param sessions
* @param sendMsg
*/
public void sendWsChat(Set<WebSocketSession> sessions,String sendMsg){
if(sessions != null && sessions.size() > 0){
for (WebSocketSession session : sessions) {
try {
session.sendMessage(new TextMessage(sendMsg));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 推送用户状态消息
*/
public void sendWsMessage(String msg){
//触发上线消息
WsMessage wm = new WsMessage();
wm.setFrom(fromUser);
wm.setMsg(msg);
wm.setMsgModel(msgModel);
users = new ArrayList<>();
sendSessions = new HashSet<>();
for(Map.Entry<String, WebSocketSession> entry : map.entrySet()){
//在线用户信息
String key = entry.getKey();
WebSocketSession session = map.get(key);
users.add(getSessionUserMap(session));
//在线用户session
sendSessions.add(session);
}
wm.setUsers(users);
for(WebSocketSession sendSession : sendSessions){
try {
sendSession.sendMessage(new TextMessage(JSON.toJSONBytes(wm)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取当前session用户的详细信息
* @param session
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, Object> getSessionUserMap(WebSocketSession session){
Map<String, Object> sessionAttrs = session.getAttributes();
this.userMap = (Map<String, Object>) sessionAttrs.get("user");
return this.userMap;
}
/**
* 获取当前session用户code
* @param session
* @return
*/
public String getSessionUser(WebSocketSession session){
userMap = getSessionUserMap(session);
if(this.userMap != null && this.userMap.size() > 0) {
return (String) userMap.get("code");
}
return null;
}
}
还建立了两个消息类,这个不是必须,是为了方便消息消息内容;
1.WsMessage.java
/**
*
*/
package com.ihome.websocket;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 用来处理系统消息
* @author zhong
*
*/
public class WsMessage {
private String type = "single"; //single,group
private String msgModel; //消息模式,如上线,离线
private String from; //来自于谁的
private String to; //发给谁的
private String msg; //消息内容
private Set<String> groups; //群组
private List<Map<String, Object>> users; //在线用户数
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public Set<String> getGroups() {
return groups;
}
public void setGroups(Set<String> groups) {
this.groups = groups;
}
public List<Map<String, Object>> getUsers() {
return users;
}
public void setUsers(List<Map<String, Object>> users) {
this.users = users;
}
public String getMsgModel() {
return msgModel;
}
public void setMsgModel(String msgModel) {
this.msgModel = msgModel;
}
}
2.WsChat.java
/**
*
*/
package com.ihome.websocket;
import java.util.Set;
/**
* 用于处理用户聊天消息
* @author zhong
*
*/
public class WsChat {
private String msgModel;// 消息类别 如:online,offline,chat
private Set<String> toUsers; //接收方
private String content;
private String from; //发送方
private Integer layer;
private String type = "single"; // 单聊/群聊 single,group
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public Integer getLayer() {
return layer;
}
public void setLayer(Integer layer) {
this.layer = layer;
}
public String getMsgModel() {
return msgModel;
}
public void setMsgModel(String msgModel) {
this.msgModel = msgModel;
}
public Set<String> getToUsers() {
return toUsers;
}
public void setToUsers(Set<String> toUsers) {
this.toUsers = toUsers;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
OK,类就写完了,接下来进行一个mvc的配置一下就好了。
在ihome.xml 文件里添加如下:
<mvc:default-servlet-handler/>
<!-- spring-websocket配置start-->
<bean id="chat" class="com.ihome.websocket.WebsocketEndPoint" />
<websocket:handlers>
<websocket:mapping path="/chat" handler="chat" />
<websocket:handshake-interceptors>
<bean class="com.ihome.websocket.HandshakeInterceptor" />
</websocket:handshake-interceptors>
</websocket:handlers>
<!-- spring-websocket配置end-->
前台核心部分,其它消息类的代码我就不贴出来了,感觉意义不大。
ps:这一版是用layer做的
var ws;
function initWs(){
//页面打开后,连接socket
var target = "ws://"+host+"/ihome/chat";
if ('WebSocket' in window) {
ws = new WebSocket(target);
console.log("WebSocket 请求成功");
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(target);
console.log("MozWebSocket 请求成功");
} else {
alert('WebSocket is not supported by this browser.');
return;
}
//注册接收消息监听方法
ws.onmessage = function(event){
console.log(event);
wsMsg = eval("("+event.data+")");
var type = wsMsg.msgModel;
if(!isOpen){
//显示im主窗口
isOpen = true;
showIm(wsMsg);
}else{
//更新好友上线或下线
if("online" == type || "offline" == type){
var imWin = window[slayero.find('iframe')[0]['name']];
imWin.updateFirendGroup(wsMsg.users);
}else{
//好友消息接收
if(undefined != wsMsg.content && "chat" == type){
var from = wsMsg.from;
var idx = getLayerIdx(from);
var body = layer.getChildFrame('body', idx);
if(undefined != body){
var $show = body.find("#msg-show");
if(undefined != $show && $show.length> 0){
var msg = {
nickname: from,
msg: wsMsg.content,
time: FY.getFormatLongDate(new Date())
};
$show.append(getMsgTemplate(msg));
//滚动条保持底部
$show.scrollTop( $show[0].scrollHeight );
}
}
}
}
}
};
ws.onclose = function (event) {
console.log(event);
};
};
//发送一条消息
function wsSend(msgs){
ws.send(JSON.stringify(msgs));
}