美文网首页Spring之路
Spring和WebSocket整合详解

Spring和WebSocket整合详解

作者: 逍遥天扬 | 来源:发表于2019-02-15 18:51 被阅读17次

    Spring和WebSocket整合详解

    官方主页

    Spring WebSocket

    概述

    WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
    WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

    HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。
    这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。
    这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

    因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。

    Web浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的HTTP连接不同,对服务器有重要的影响。

    基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。

    这里,我们基于Spring整合原生的WebSocket,实现简单的IM聊天功能。

    Git地址:
    Gitee

    项目地址:
    品茗IT-Spring之路专题

    品茗IT:提供在线快速构建Spring项目工具。

    开始搭建

    依赖Jar包

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
        <version>${spring.version}</version>
    </dependency>
    

    spring-websocket.xml

    该xml文件仅作为配置文件的引入使用。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
                         http://www.springframework.org/schema/beans
                         http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                         http://www.springframework.org/schema/aop
                         http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                         http://www.springframework.org/schema/context
                         http://www.springframework.org/schema/context/spring-context-4.0.xsd ">
    
        <bean id="annotationPropertyConfigurerWebSocket"
              class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="order" value="1" />
            <property name="ignoreUnresolvablePlaceholders" value="true" />
            <property name="locations">
                <list>
                    <value>classpath:websocket.properties</value>
                </list>
            </property>
        </bean>
        
    </beans>
    

    websocket.properties保存一些自定义配置,这里没用上,就不贴出来了。

    WebSocket配置监听

    Spring整合WebSocket需要配置websocket的监听url。

    SpringWebSocketConfig:

    package com.cff.springwork.websocket.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.socket.config.annotation.EnableWebSocket;
    import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
    import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
    import org.springframework.web.socket.handler.TextWebSocketHandler;
    
    import com.cff.springwork.websocket.handler.WebsocketHandler;
    
    
    @Configuration
    @EnableWebMvc
    @EnableWebSocket
    public class SpringWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
        public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
            registry.addHandler(webSocketHandler(),"/websocket").addInterceptors(new WebSocketInterceptor());
            registry.addHandler(webSocketHandler(), "/sockjs").addInterceptors(new WebSocketInterceptor()).withSockJS();
        }
     
        @Bean
        public TextWebSocketHandler webSocketHandler(){
            return new WebsocketHandler();
        }
    
    }
    

    WebSocket的session拦截处理

    WebSocketInterceptor对WebSocket的连接进行过滤,可以对连接前和连接后自定义处理。

    WebSocketInterceptor:

    package com.cff.springwork.websocket.config;
    
    import java.util.Map;
    
    import javax.servlet.http.HttpSession;
    
    import org.springframework.http.server.ServerHttpRequest;  
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.http.server.ServletServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.web.socket.WebSocketHandler;  
    import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  
    
    @Component
    public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor{  
      
        @Override  
        public boolean beforeHandshake(ServerHttpRequest request,  
                ServerHttpResponse response, WebSocketHandler wsHandler,  
                Map<String, Object> attributes) throws Exception {  
            System.out.println("Before Handshake");  
            if (request instanceof ServletServerHttpRequest) {
                ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
                HttpSession session = servletRequest.getServletRequest().getSession(false);
                if (session != null) {
                    //使用userName区分WebSocketHandler,以便定向发送消息
                    String userName = (String) session.getAttribute("userId");
                    if (userName==null) {
                        userName="default";
                    }
                    attributes.put("userId",userName);
                }
            }
            return super.beforeHandshake(request, response, wsHandler, attributes);  
        }  
      
        @Override  
        public void afterHandshake(ServerHttpRequest request,  
                ServerHttpResponse response, WebSocketHandler wsHandler,  
                Exception ex) {  
            System.out.println("After Handshake");  
            super.afterHandshake(request, response, wsHandler, ex);  
        }  
      
    }  
    

    消息处理(处理逻辑配合下面的聊天室)

    WebsocketHandler负责处理消息发送接收的逻辑:

    package com.cff.springwork.websocket.handler;
    
    import java.io.IOException;
    import java.util.List;
    
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.socket.BinaryMessage;
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketMessage;
    import org.springframework.web.socket.WebSocketSession;  
    import org.springframework.web.socket.handler.TextWebSocketHandler;
    
    import com.cff.springwork.websocket.memory.WebSocketUser;
      
    public class WebsocketHandler extends TextWebSocketHandler {  
        protected Logger logger = LoggerFactory.getLogger(getClass());
        public static WebSocketUser users = new WebSocketUser();
        public WebsocketHandler(){
            
        }
        
        
        @Override  
        protected void handleTextMessage(WebSocketSession session,  
                TextMessage message) throws Exception {  
            logger.info("websocketmsg:{}",message.toString()); 
            super.handleTextMessage(session, message);  
            String msg = message.getPayload();
            logger.info("websocketmsg1:{}",msg); 
            if(StringUtils.isNotEmpty(msg)){
                String param[] = msg.split("\\|");
                String userName = (String)session.getAttributes().get("userName");
                sendMessageToUser(param[1], new TextMessage(param[0]+"|"+userName+"|"+param[2]));
            }
    //        TextMessage returnMessage = new TextMessage(message.getPayload()+" received at server");  
    //        session.sendMessage(returnMessage);  
        }
    
        @Override
        public void afterConnectionEstablished(WebSocketSession session) throws Exception {
            String userName = (String)session.getAttributes().get("userName");
            String msgType="0000";
            if(userName == null || "".equals(userName)){
                long id = users.getWebsocketId();
                logger.info("random UserId:{}",id); 
                users.putUserNameAndWebSocketSession(id+"", session);
                
                session.sendMessage(new TextMessage(msgType+"|"+ id));
            }else{
                logger.info("UserName:{}",userName); 
                //users.putNickNameAndWebSocketSession(userInfo.getNickName(), session);
                users.putUserNameAndWebSocketSession(userName, session);
                session.sendMessage(new TextMessage(msgType+"|"+userName));
            }
            
    
            List<TextMessage> message = WebsocketHandler.getOfflineMsg(userName);
            if( message !=null && message.size()!=0){
                for(int i = 0; i < message.size(); i++){
                    WebsocketHandler.sendMessageToUser(userName, message.get(i));
                }
            }
        }
    
        @Override
        public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    //      
    //      super.afterConnectionClosed(session, status);
            users.removeWebSocketSession(session);
            
        }
        
        /**
         * 给某个用户发送消息
         *
         * @param userName
         * @param message
         */
        public static void sendMessageToUser(String userName, TextMessage message) {
            List<WebSocketSession> webUsers = users.getWebSocketSessionWithUserName(userName);
            System.out.println(webUsers);
            System.out.println(userName);
            if (webUsers == null || webUsers.size() == 0){
                users.putOfflineMsg(userName, message);
                return;
            }
            for(int i =0; i < webUsers.size(); i++){
                WebSocketSession user = webUsers.get(i);
                System.out.println(user);
                
                try {
                    if (user.isOpen()) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } 
            }
        }
        
        
        /**
         * 给某个用户发送消息
         *
         * @param userName
         * @param message
         */
        public static void sendBinaryMessageToUser(String userName, BinaryMessage message) {
            List<WebSocketSession> webUsers = users.getWebSocketSessionWithUserName(userName);
            System.out.println(webUsers);
            System.out.println(userName);
            if (webUsers == null || webUsers.size() == 0){
                //users.putOfflineMsg(userName, message);
                return;
            }
            for(int i =0; i < webUsers.size(); i++){
                WebSocketSession user = webUsers.get(i);
                System.out.println(user);
                
                try {
                    if (user.isOpen()) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } 
            }
        }
        
        public static List<TextMessage> getOfflineMsg(String userName){
            return users.getOfflineMsgWithUserName(userName);
        }
    }  
    

    WebSocketUser是使用内存来存储用户:

    package com.cff.springwork.websocket.memory;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketSession;
    
    public class WebSocketUser {
        private static Map<String,List<WebSocketSession>> userNameWebsession = null;
        private static Map<WebSocketSession,String> webSessionUserName = null;
        private static Map<String,List<TextMessage>> userNameWithOfflineMsg = null;
        private static long websocketId = 100000000L;
        
        public WebSocketUser(){
            userNameWebsession = new ConcurrentHashMap<String,List<WebSocketSession>>();
            webSessionUserName = new ConcurrentHashMap<WebSocketSession,String>();
            userNameWithOfflineMsg = new ConcurrentHashMap<String,List<TextMessage>>();
        }
        
        public Map<String,List<WebSocketSession>> getUserNameWebsession(){
            return userNameWebsession;
        }
        
        public Map<WebSocketSession,String> getWebSessionUserName(){
            return webSessionUserName;
        }
        
        public String getUserNameWithWebSocketSession(WebSocketSession webSocketSession){
            return webSessionUserName.get(webSocketSession); 
        }
    
        public static Map<String, List<TextMessage>> getUserNameWithOfflineMsg() {
            return userNameWithOfflineMsg;
        }
    
        public static void setUserNameWithOfflineMsg(Map<String, List<TextMessage>> userNameWithOfflineMsg) {
            WebSocketUser.userNameWithOfflineMsg = userNameWithOfflineMsg;
        }
    
        public static void setUserNameWebsession(Map<String, List<WebSocketSession>> userNameWebsession) {
            WebSocketUser.userNameWebsession = userNameWebsession;
        }
    
        public static void setWebSessionUserName(Map<WebSocketSession, String> webSessionUserName) {
            WebSocketUser.webSessionUserName = webSessionUserName;
        }
    
        /**
         * 根据昵称拿WebSocketSession
         * @param nickName
         * @return
         */
        public List<WebSocketSession> getWebSocketSessionWithUserName(String userName){
            return userNameWebsession.get(userName);
        }
        
        /**
         * 移除失效的WebSocketSession
         * @param webSocketSession
         */
        public synchronized void removeWebSocketSession(WebSocketSession webSocketSession){
            if (webSocketSession == null) return;
            String nickName = webSessionUserName.get(webSocketSession);
            webSessionUserName.remove(webSocketSession);
            List<WebSocketSession> webSessoin = userNameWebsession.get(nickName);
            if (webSessoin == null) return;
            webSessoin.remove(webSocketSession);
        }
        
        /**
         * 存放离线消息
         * @param nickName
         * @param msg
         */
        public synchronized void putOfflineMsg(String userName, TextMessage msg){
            if (userNameWithOfflineMsg.get(userName) == null){
                List<TextMessage> msgList = new ArrayList<TextMessage>();
                msgList.add(msg);
                userNameWithOfflineMsg.put(userName, msgList);
            }else{
                List<TextMessage> msgList = userNameWithOfflineMsg.get(userName);
                msgList.add(msg);
            }
        }
        
        /**
         * 根据昵称拿离线消息
         * @param nickName
         * @return
         */
        public List<TextMessage> getOfflineMsgWithUserName(String userName){
            return userNameWithOfflineMsg.get(userName);
        }
        
        /**
         * 存放昵称和WebSocketSession
         * @param nickName
         * @param webSocketSession
         */
        public synchronized void putUserNameAndWebSocketSession(String userName, WebSocketSession webSocketSession){
            webSessionUserName.put(webSocketSession, userName);
            if (userNameWebsession.get(userName) == null){
                List<WebSocketSession> webSessoin = new ArrayList<WebSocketSession>();
                webSessoin.add(webSocketSession);
                userNameWebsession.put(userName, webSessoin);
            }else{
                List<WebSocketSession> webSessoin = userNameWebsession.get(userName);
                webSessoin.add(webSocketSession);
            }
        }
        
        /**
         * 移除失效的WebSocketSession
         * @param webSocketSession
         */
        public synchronized long getWebsocketId(){
            websocketId += 1;
            return websocketId;
        }
        
        public static void main(String srga[]){
            String test = "123456";
            
            Boolean flag = true;
            if(test == "012345678".substring(1, 7)){
                flag = false;
            }
            System.out.println(flag);
        }
    }
    

    聊天室

    为了实现我们的简单聊天功能,我们需要前端进行配合。

    websocketchat.jsp实现了简单的聊天室,支持文字、表情、文件等:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <%@ include file="/common/head.jsp"%>
    <!-- <script src="http://www.jq22.com/js/jq.js"></script> -->
    <script type="text/javascript" src="${ctx}/js/jquery/jquery-3.2.1.min.js" ></script>
    <script src="${ctx}/js/websocket/websocket.js"></script>
    <script src="${ctx}/js/jquery/ajaxfileupload.js"></script>
    <script src="${ctx}/js/jquery/jquery-browser.js"></script>
    <script src="${ctx}/js/jquery/jquery.qqFace.js"></script>
    <head>
    <title>聊天室</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="content-script-type" content="text/javascript">
    <meta http-equiv="content-style-type" content="text/css">
    <style rel="stylesheet" type="text/css" media="all" />
    body { text-align:left; margin:0; font:normal 12px Verdana, Arial;
    background:#FFEEFF } form { margin:0; font:normal 12px Verdana,
    Arial; } table,input { font:normal 12px Verdana, Arial; }
    a:link,a:visited{ text-decoration:none; color:#333333; } a:hover{
    text-decoration:none; color:#FF6600 } #main { width:400px;
    position:absolute; left:600px; top:100px; background:#EFEFFF;
    text-align:left; filter:Alpha(opacity=90) } #ChatHead {
    text-align:right; padding:3px; border:1px solid #003399;
    background:#DCDCFF; font-size:20px; color:#3366FF; cursor:move; }
    #ChatHead a:link,#ChatHead a:visited, { font-size:14px;
    font-weight:bold; padding:0 3px } #ChatBody { border:1px solid
    #003399; border-top:none; padding:2px; } #ChatContent {
    height:200px; padding:6px; overflow-y:scroll; word-break: break-all
    }#ChatBtn { border-top:1px solid #003399; padding:2px }
    </style>
    <script language="javascript" type="text/javascript">
    var ws = null;
    var msgUser=null;
    var muserId = null;
    var nickName ="";
    var contextPath = '${ctx}';
    var imgName = null;
    var fileImgSize = 0;
    window.onbeforeunload = function()
    {
        disconnect(ws);
    }
    function ChatSetUser(user,userId) {
        if(muserId != null && muserId != userId){
            $("#ChatContent").html("");
        }
        msgUser = user;
        muserId = userId;
        imgName = msgUser;
        //alert("owerId:" + nickName + "-----msgUser:" + msgUser);
        //alert("userId:" + nickName + "-----userId:" + muserId);
        if (msgUser == nickName) {
            return;
        }
        if (msgUser == "") {
            return;
        }
        ChatNew();
    }
    function gs(d) {
        var t = document.getElementById(d);
        if (t) {
            return t.style;
        } else {
            return null;
        }
    }
    function gs2(d, a) {
        if (d.currentStyle) {
            var curVal = d.currentStyle[a]
        } else {
            var curVal = document.defaultView
                    .getComputedStyle(d, null)[a]
        }
        return curVal;
    }
    function ChatHidden() {
        gs("ChatBody").display = "none";
    }
    function ChatShow() {
        gs("ChatBody").display = "";
    }
    function ChatClose() {
        gs("main").display = "none";
        //disconnect(ws);
    }
    function ChatNew() {
        gs("main").display = "";
        $("#ChatUsers").html(msgUser);
        $('.emotion').qqFace({
    
            id : 'facebox', 
    
            assign:'saytext', 
    
            path: contextPath+'/img/arclist/'   //表情存放的路径
    
        });
    }
    function ChatClear(obj) {
        $("#ChatContent").html("");
    }
    
    function ChatRead() {
        document.getElementById(msgUser).setAttribute('src', contextPath+'/img/users.png');
    }
    
    function ChatSend(obj) {
        var o = obj.ChatValue;
        var msg = replace_em(o.value);
        if (o.value.length > 0) {
            $("#ChatContent").append(
                    "<p align=\"right\"><strong>" + nickName + "(我) :</strong>" + msg
                            + "</p>");
            var number = $("#ChatContent").scrollTop();
            number += 16;
            $("#ChatContent").scrollTop(number);
            if(ws!=null){
                ws.send("0001" + "|" + muserId + "|" + encodeURI(o.value));
            }
            o.value = '';
        }
        
        var img = obj.ChatFile;
        if (img.value.length > 0){
            $("#ChatContent").append(
                    "<p align=\"right\"><strong>" + nickName + "(我) :</strong>" + img.value
                            + "</p><br/>");
    
            imgName = nickName+'(我)';
            fileImgSize = img.files.length;
            //alert(fileImgSize);
            $.ajaxFileUpload({
                //处理文件上传操作的服务器端地址(可以传参数,已亲测可用)
                url:'${ctx}/webSocket/fileUpload?userId='+muserId,
                secureuri:true,                       //是否启用安全提交,默认为false 
                fileElementId:'ChatFile',           //文件选择框的id属性
                dataType:'text',                       //服务器返回的格式,可以是json或xml等
                success:function(data, status){        //服务器响应成功时的处理函数
                    //$("#ChatContent").append("<p align=\"right\">" + data + "</p><br/>");
                },
                error:function(data, status, e){ //服务器响应失败时的处理函数
                    $("#ChatContent").append('<p align=\"right\">图片上传失败,请重试!!</p><br/>');
                    imgName = msgUser;
                }
            });
        }
    }
        if (document.getElementById) {
            (function() {
                if (window.opera) {
                    document.write("<input type='hidden' id='Q' value=' '>");
                }
    
                var n = 500;
                var dragok = false;
                var y, x, d, dy, dx;
    
                function move(e) {
                    if (!e)
                        e = window.event;
                    if (dragok) {
                        d.style.left = dx + e.clientX - x + "px";
                        d.style.top = dy + e.clientY - y + "px";
                        return false;
                    }
                }
    
                function down(e) {
                    if (!e)
                        e = window.event;
                    var temp = (typeof e.target != "undefined") ? e.target
                            : e.srcElement;
                    if (temp.tagName != "HTML" | "BODY"
                            && temp.className != "dragclass") {
                        temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
                                : temp.parentElement;
                    }
                    if ('TR' == temp.tagName) {
                        temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
                                : temp.parentElement;
                        temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
                                : temp.parentElement;
                        temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
                                : temp.parentElement;
                    }
    
                    if (temp.className == "dragclass") {
                        if (window.opera) {
                            document.getElementById("Q").focus();
                        }
                        dragok = true;
                        temp.style.zIndex = n++;
                        d = temp;
                        dx = parseInt(gs2(temp, "left")) | 0;
                        dy = parseInt(gs2(temp, "top")) | 0;
                        x = e.clientX;
                        y = e.clientY;
                        document.onmousemove = move;
                        return false;
                    }
                }
    
                function up() {
                    dragok = false;
                    document.onmousemove = null;
                }
    
                document.onmousedown = down;
                document.onmouseup = up;
    
            })();
        }
        
        function toIndex(){
            window.location.href= contextPath + "/index.jsp";
        }
    
        $(function() {
            if (ws == null) {
                var url = getUrl();
                //alert("url:"+url);  
                if (!url) {  
                    return;  
                }  
    
                ws = new WebSocket(url);  
                connect(ws);
            }
            ChatClose();
    //      ChatSetUser("human","16888888888");
        })
    </script>
    </head>
    
    <body>
        <div>
            <div style="display: inline;">
                <img style="width: 5%;margin-left: 100px;" alt="" src="${ctx}/img/logo.png" onclick="toIndex()"/>
            </div>
            <div style="display: inline; float: right">
                <img style="width: 220px;display:block;margin-top: 15px;margin-right: 200px;"  alt="" src="${ctx}/img/logoright.png"/>
            </div>
        </div>
        <div id="main" class="dragclass" onclick="ChatRead()" style="left: 400px; top: 200px;">
            <div id="ChatUsers" style="width:100px; padding:3px; font-size:15px;float:left; display:inline"></div>
            <div id="ChatHead">
                <a href="#" onclick="ChatHidden();">-</a> <a href="#"
                    onclick="ChatShow();">+</a> <a href="#" onclick="ChatClose();">x</a>
            </div>
            <div id="ChatBody">
                <div id="ChatContent"></div>
                <div id="ChatBtn">
                    <form action="" name="chat" method="post">
                        <textarea name="ChatValue" id="saytext" rows="3" style="width: 350px"></textarea>
                        <input name="Submit" type="button" value="发送"
                            onclick="ChatSend(this.form);" />
                        <input name="ClearMsg" type="button" value="清空记录"
                            onclick="ChatClear(this.form);" />
                        <input type="button" class="emotion" value="表情">
                        <input id="ChatFile" type="file" name="myfiles"  multiple>   
                    </form>
                </div>
            </div>
        </div>
        <div align="left" style="margin-top: 50px;margin-left: 20px;">
            <p id=RandomContent>欢迎您,15607110725</p>
            <p id=content></p>
        </div>
        
    </body>
    </html>
    

    这个jsp需要websocket.js配合:

    function getUrl() {     
        var index = contextPath.lastIndexOf("/");
        var urlPath = contextPath.substring(index, contextPath.length) + "/websocket";
        if (window.location.protocol == 'http:') {  
            url = 'ws://' + window.location.host + urlPath;  
        } else {  
            url = 'wss://' + window.location.host + urlPath;  
        }   
        return url;
    } 
    
    function disconnect(ws) {  
        if (ws != null) {  
            ws.close();  
            ws = null;  
        }  
    }  
    
    function replace_em(str){
    
    //  str = str.replace(/\</g,'&lt;');
    //
    //  str = str.replace(/\>/g,'&gt;');
    //
    //  str = str.replace(/\n/g,'<br/>');
    
        str = str.replace(/\[em_([0-9]*)\]/g,'<img src="../../img/arclist/$1.gif" border="0" />');
    
        return str;
    
    }
    
    function connect(ws) { 
    
        ws.onopen = function () {  
            $("#ChatContent").append("<small>连接成功。。。</small><br>");
        };  
        ws.onmessage = function (event) {
            if(typeof(event.data)=="string"){  
                var dataAll = event.data;
                var indexMsgType = dataAll.indexOf("|");
                var msgType = dataAll.substring(0,indexMsgType);
                console.log('dataAll:'+dataAll);
                if(msgType == "0000"){
                    webid = dataAll.substring(indexMsgType+1,dataAll.length);
                    $("#RandomContent").html("欢迎您,"+webid);
                    nickName = webid;
                }else{
                    var data = dataAll.substring(indexMsgType+1,dataAll.length);
                    var index = data.indexOf("|");
                    var userId = data.substring(0,index);
                    var msg = decodeURI(data.substring(index+1,data.length));
                    var result = replace_em(msg);
                    if(document.getElementById(userId)){
                        document.getElementById(userId).setAttribute('src', contextPath+'/img/msgget.gif');
                        var number = $("#ChatContent").scrollTop();
                        //var number = $("#ChatContent").height();
                        number += 15;
                        $("#ChatContent").scrollTop(number);
                        $("#ChatContent").append("<strong>"+userId+" :</strong>" + result + "<br>");
                    }else{
                        //var content = $("#content").html();
                        content = "<img src=\""+contextPath + "/img/msgget.gif\" id=\""
                            + userId
                            + "\" alt=\"\" style=\"cursor: pointer\" width='40px' "
                            + "onclick=\"ChatSetUser('"
                            + userId
                            + "','"
                            + userId
                            + "')\" />"
                            + userId
                            + "<br><br>";
                        $("#content").append(content);
                        $("#ChatContent").append("<strong>"+userId+" :</strong>" + result + "<br>");
                    }
                    
                }
            }else{  
              var reader = new FileReader();  
              reader.onload = function(event){  
                   if(event.target.readyState == FileReader.DONE){  
                        var url = event.target.result;  
                        if (imgName != msgUser){
                            $("#ChatContent").append("<p align=\"right\"><strong>"+imgName+" :</strong>"+"<img src = "+url+" width='100px'/></p><br>");  
                        }else{
                            $("#ChatContent").append("<strong>"+imgName+" :</strong>"+"<img src = "+url+" width='100px'/><br>");  
                        }
                        if (fileImgSize != 0){
                            fileImgSize = fileImgSize - 1;
                        }else{
                            imgName = msgUser;
                        }
                   }
               }  
              reader.readAsDataURL(event.data);  
            }  
        };  
        ws.onclose = function (event) {  
            //alert('网络连接失败!');  
        };  
    } 
    

    以上已经支持表情和文字发送,要支持图片,需要我们自定义发送文件功能WebSocketController:

    package com.cff.springwork.websocket.endpoint;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.socket.BinaryMessage;
    
    import com.cff.springwork.websocket.handler.WebsocketHandler;
    
    @Controller("webSocketController")
    @RequestMapping("/webSocket")
    public class WebSocketController {
        protected Logger logger = LoggerFactory.getLogger(getClass());
        
        @RequestMapping(value="/fileUpload")
        public void fileUpload(@RequestParam("userId") String userId, @RequestParam MultipartFile[] myfiles, HttpServletRequest request, HttpServletResponse response) throws IOException{
            //可以在上传文件的同时接收其它参数
            System.out.println("收到发往用户[" + userId + "]的文件上传请求");
            
            //如果用的是Tomcat服务器,则文件会上传到\\%TOMCAT_HOME%\\webapps\\YourWebProject\\upload\\文件夹中
            //这里实现文件上传操作用的是commons.io.FileUtils类,它会自动判断/upload是否存在,不存在会自动创建
            String realPath = request.getSession().getServletContext().getRealPath("/upload");
            System.out.println("路径[" + realPath + "]的文件上传请求");
            System.out.println("文件数量:"+myfiles.length);
            realPath = "D:/doc/";
            //设置响应给前台内容的数据格式
            response.setContentType("text/plain; charset=UTF-8");
            //上传文件的原名(即上传前的文件名字)
            String originalFilename = null;
            //如果只是上传一个文件,则只需要MultipartFile类型接收文件即可,而且无需显式指定@RequestParam注解
            //如果想上传多个文件,那么这里就要用MultipartFile[]类型来接收文件,并且要指定@RequestParam注解
            //上传多个文件时,前台表单中的所有<input type="file"/>的name都应该是myfiles,否则参数里的myfiles无法获取到所有上传的文件
            for(MultipartFile myfile : myfiles){
                if(myfile.isEmpty()){
                    response.setCharacterEncoding("UTF-8");
                    response.getWriter().write("102");
                    return;
                }else{
                    originalFilename = myfile.getOriginalFilename();
                    System.out.println("文件原名: " + originalFilename);
                    System.out.println("文件名称: " + myfile.getName());
                    System.out.println("文件长度: " + myfile.getSize());
                    System.out.println("文件类型: " + myfile.getContentType());
                    System.out.println("========================================");
                    try {
                        //这里不必处理IO流关闭的问题,因为FileUtils.copyInputStreamToFile()方法内部会自动把用到的IO流关掉
                        //此处也可以使用Spring提供的MultipartFile.transferTo(File dest)方法实现文件的上传
                        //FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, originalFilename));
                        
                        ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); 
                        byte[] buff = new byte[100]; //buff用于存放循环读取的临时数据 
                        int rc = 0; 
                        InputStream is = myfile.getInputStream();
                        while ((rc = is.read(buff, 0, 100)) > 0) { 
                        swapStream.write(buff, 0, rc); 
                        } 
                        byte[] in_b = swapStream.toByteArray(); //in_b为转换之后的结果
                        System.out.println("正在发送文件: ");
    
                        WebsocketHandler.sendBinaryMessageToUser(userId,new BinaryMessage(in_b));
                    } catch (IOException e) {
                        System.out.println("文件[" + originalFilename + "]上传失败,堆栈轨迹如下");
                        e.printStackTrace();
                        response.setCharacterEncoding("UTF-8");
                        response.getWriter().write("101");
                        return ;
                    }
                }
            }
            response.setCharacterEncoding("UTF-8");
            response.getWriter().write("200");
            return;
        }
        
    }
    

    详细完整代码,可以在Spring组件化构建中选择查看,并下载。

    快速构建项目

    Spring组件化构建

    相关文章

      网友评论

        本文标题:Spring和WebSocket整合详解

        本文链接:https://www.haomeiwen.com/subject/qabmeqtx.html