Spring + Websocket 搭建的仿QQ内聊工具

9 篇文章 0 订阅
1 篇文章 0 订阅

由于项目中需要增加一个内聊工具,以前用过 comet 实现过一些消息推送的东西,总感觉不是最合理的方式。是于我搜索发现spring也有websocket这个东西,发现就是我想要的。

经过折腾算是可以实现可以点对点通讯,或点对多的通讯,也可以跟spring项目整合在一起。 为了后续备查,就把这个草稿就记录下:

首先,引入websocket的jar包:
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));
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值