spring websocket

在web项目中尝尝需要涉及到数据实时更新的需求,如后台监控数据,需要实时显示的进度信息,扫描信息等,之前实现的方式大部分都采用了长连接的形式,如dwr,效率较高的websocket在IE10以下版本中不受支持,让人遗憾,现在spring 4.0提供了sockjs的支持,它会根据浏览器是否支持websocket创建不同的连接,而且无需开发人员写多余的代码,解决了websocket功能在IE10以下中的问题,废话少说自己写了一个简易群聊demo(下载源码地址http://download.csdn.net/detail/u011411069/9486431),看代码说话

因消息内容需显示实时时间故继承之前已有的工具类

/**
 * Created by zhang on 2016/4/3.
 */
public class MsgUtils {

    static DateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 当前时间
     * @return
     */
    public static String nowTime(){
        return df.format(new Date());
    }
}

/**
 * Created by zhang on 2016/4/3.服务端的websocket
 */
public class MsgSocketJ extends MsgUtils implements WebSocketHandler {

    static ConcurrentHashMap<String,WebSocketSession> clients=new ConcurrentHashMap<>();
    //此种方式的websocket服务端为单例,成员变量需注意<span style="white-space:pre">	</span>
    static Logger logger = LoggerFactory.getLogger(MsgSocketJ.class);
    static ConcurrentHashMap<String,SessionBean> localInfo=new ConcurrentHashMap<>();
    /**
     * 握手成功后执行的方法
     * @param webSocketSession
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        logger.debug("A new client connected!!!");
        SessionBean sessionBean=new SessionBean();//自己封装的Session实体
        sessionBean.setSession(webSocketSession);
        localInfo.put(webSocketSession.getId(),sessionBean);//记录客户端session
    }

    /**
     * 接收到消息后执行的方法
     * @param webSocketSession
     * @param webSocketMessage
     * @throws Exception
     */
    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
        String message= (String) webSocketMessage.getPayload();
        if(message.startsWith("$$username")){
            localInfo.get(webSocketSession.getId()).setUsername(message.split("=")[1]);
            clients.put(webSocketSession.getId(),webSocketSession);
            broadcast(nowTime()+" 服务器消息"+ ":欢迎\""+localInfo.get(webSocketSession.getId()).getUsername()+"\"加入聊天;",true,webSocketSession);
            userList();
        }else{
            broadcast(nowTime()+" "+localInfo.get(webSocketSession.getId()).getUsername()+":"+message,false,webSocketSession);
        }
    }

    /**
     * 发生异常时执行的方法
     * @param webSocketSession
     * @param throwable
     * @throws Exception
     */
    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        logger.error("An error triggered!!!");
        logger.error(throwable.getMessage(),throwable);
        clients.remove(webSocketSession.getId());
        broadcast(nowTime()+" 服务器消息"+":\""+localInfo.get(webSocketSession.getId()).getUsername()+"\"离开聊天室;",true,webSocketSession);
        userList();
        localInfo.remove(webSocketSession.getId());
    }

    /**
     * 连接断开时执行的方法
     * @param webSocketSession
     * @param closeStatus
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        logger.debug("A client has disconnected!!!");
        clients.remove(webSocketSession.getId());//移除离线的session
        broadcast(nowTime()+" 服务器消息"+":\""+localInfo.get(webSocketSession.getId()).getUsername()+"\"离开聊天室;",true,webSocketSession);
        userList();
        localInfo.remove(webSocketSession.getId());
    }

    /**
     * 是否允许部分消息
     * 设为true后调用webSocketMessage.getPayload()后可能读到的只是一部分消息
     * 可调用webSocketMessage.isLast()判断是否读完
     * @return
     */
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 广播消息
     * @param msg
     */
    public static void broadcast(String msg, boolean isServer, WebSocketSession curr){
        for (Map.Entry<String,WebSocketSession> entry:clients.entrySet()){
            try {
                if(entry.getValue().isOpen()){
                    if(!isServer&&entry.getValue()==curr)
                        continue;//跨过自己发送的消息
                    entry.getValue().sendMessage(new TextMessage(msg));
                }else{
                    clients.remove(entry.getKey());
                }
            } catch (IOException e) {
                logger.error(e.getMessage(),e);
            }
        }
    }

    /**
     * 广播用户列表
     */
    public static void userList(){
        StringBuffer buffer=new StringBuffer();
        boolean notEmpty=false;
        for (Map.Entry<String,WebSocketSession> entry:clients.entrySet()) {
            if(entry.getValue().isOpen()){
                notEmpty=true;
                buffer.append(localInfo.get(entry.getValue().getId()).getUsername()+",");
            }
        }
        if(notEmpty){
            String list=buffer.toString();
            list="$$userList"+list.substring(0,list.length());
            broadcast(list,true,null);
        }
    }

    /**
     * 每10秒广播一次用户列表
     */
    static Timer timer=new Timer();
    static{
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                userList();
            }
        },100,10000);
    }
}
/**
 * Created by zhang on 2016/4/3.
 * 注册websocket
 */
@Configuration
@EnableWebMvc
@EnableWebSocket
public class SockJSConfig implements WebSocketConfigurer {
	 @Override 
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { 
					//addInterceptors是添加握手前的拦截器,可不添加,withSockJS,开启sockJS支持 
		registry.addHandler(msgSocketJ(), "/msg").addInterceptors(new WebSocketHandshakeInterceptor()) .withSockJS();
	}

	@Bean 
	public MsgSocketJ msgSocketJ(){ 
		return new MsgSocketJ();//这种@Configuration+@Bean与在xml中配置<bean />,所以与原生websocket不一样的地方就是服务端是一个单例 
	}
}
/**
 * Created by zhang on 2016/4/3.
 */
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
        //在握手前执行一些操作,可拒绝连接
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
        //握手后执行
    }
}
web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!--版本需3.0以上,<async-supported>支持-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" >

  <display-name>Archetype Created Web Application</display-name>

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath*:context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <!--在websocket请求中会过的servlet,filter中必须加入,不然非原生websocket会报错-->
    <async-supported>true</async-supported>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/sockjs/*</url-pattern>
  </servlet-mapping>

</web-app>
前台代码

<!DOCTYPE html>
<html>
<head>
    <title>Hello SockJS!</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script type="text/javascript" src="js/jquery-1.12.2.js"></script>
    <script type="text/javascript" src="js/sockjs-1.0.3.js"></script>
</head>
<body>
<h2>Hello SockJS!</h2>
<h2 id="h"></h2>
<label>username:</label><input name="username" maxlength="5" type="text" /><button id="login" οnclick="createWs();">login</button>
<div style="width:810px;height:500px;">
    <div style="width:605px;height:480px;float:left;">
        <div id="div" style="border:1px solid red;height:400px; width:600px;overflow-y:auto;border-radius:5px;"></div>
        <textarea id="msg" style="width:596px;height:50px;resize:none;border-radius:5px;display:block;"></textarea>
        <input type="button" value="send" οnclick="sendMsg();" />
        <input type="button" value="closeWs" οnclick="closeWs();" />
    </div>
    <div id="userList" style="width:200px;height:455px;border:1px solid red;float:left;border-radius:5px;"></div>
</div>
<script type="text/javascript">
    var ws,username;

    function createWs() {
        if($("[name=username]").val().trim().length==0){
            alert("please enter your name!");
            return;
        }

        $("#login").attr("disabled","disabled");
        $("[name=username]").attr("disabled","disabled");
        username=$("[name=username]").val();

        ws = new SockJS("http://localhost:8080/web/sockjs/msg");
        //连接成功后前台触发的函数
        ws.onopen = function () {
            document.getElementById("h").innerHTML="joined successful!!!";
            ws.send("$$username="+$("[name=username]").val());
        };
        //接收到后台推送的消息后触发的函数
        ws.onmessage = function (evt) {
            if(evt.data.indexOf("$$userList")==0){
                var list=evt.data.replace("$$userList","")
                $("#userList").empty().append(replaceAllStr(list,",","<br />"))
            }else{
                document.getElementById("div").innerHTML+=evt.data+"<br />";
            }
        };
        //websocket断开连接时触发的函数
        ws.onclose = function (evt) {
            document.getElementById("h").innerHTML="WebSocketClosed!";
        };
        //发生异常时触发的函数
        ws.onerror = function (evt) {
            document.getElementById("h").innerHTML="WebSocketError!";
        };
    }

    /**
     * 断开连接
     */
    function closeWs() {
        if(ws){
            ws.close();
            ws=null;
            $("#login").removeAttr("disabled");
            $("[name=username]").removeAttr("disabled");
        }
    }

    /**
     * 发送消息
     */
    function sendMsg(){
        if(ws){
            ws.send($("#msg").val());
            document.getElementById("div").innerHTML+=nowTime()+" "+username+":"+$("#msg").val()+"<br />";
            $("#msg").val("");
        }
    }

    /**
     * 当前时间
     * @returns {string}
     */
    function nowTime() {
        var date=new Date();
        var MM=date.getMonth()<10?"0"+(date.getMonth()+1):date.getMonth()+1;
        var dd=date.getDate()<10?"0"+(date.getDate()+1):date.getDate()+1;
        var hh=date.getHours()<10?"0"+date.getHours():date.getHours();
        var min=date.getMinutes()<10?"0"+date.getMinutes():date.getMinutes();
        var ss=date.getSeconds()<10?"0"+date.getSeconds():date.getSeconds();
        return date.getFullYear()+"-"+MM+"-"+dd+" "+hh+":"+min+":"+ss;
    }

    function replaceAllStr(Str,old,New){
        while (Str.indexOf(old)!=-1){
            Str=Str.replace(old,New);
        }
        return Str;
    }
</script>
</body>
</html>
用到的依赖(日志未添加)
<spring.version>4.1.2.RELEASE</spring.version>
    <jackson.version>2.3.1</jackson.version>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-websocket</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值