Spring4+WebSocket实例

42 篇文章 0 订阅

WebSocket是H5的新特性,用于客户端和服务端的实时消息交互,例如聊天室。有些WEB容器的新版本也对websocket提供了支持。例如,resin4以上版本,tomcat8以上版本。但是如果你不想升级你的Web容器版本,也可以实现,就是spring4以上版本支持websocket。

一、先从web.xml配置文件说起,在web.xml里面需要做的事情就是让websocket请求走springmvc的过滤器。配置如下:

<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:springmvc-default.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.htm</url-pattern><!-- 这是普通的请求 -->
<url-pattern>/ws/*</url-pattern><!-- 这是websocket的请求 -->
</servlet-mapping>


二、在applicationContext.xml中声明websocket处理Bean,配置如下:

在头部添加xmlns:websocket="http://www.springframework.org/schema/websocket"

http://www.springframework.org/schema/websocket   http://www.springframework.org/schema/websocket/spring-websocket.xsd

配置

<bean id="websocket" class="cn.remotejob.websocket.WebSocketEndPoint"/>  
  
<websocket:handlers allowed-origins="*">  
   <websocket:mapping path="/websocket.ws" handler="websocket"/>  
   <websocket:handshake-interceptors>  
    <bean class="cn.remotejob.websocket.HandShakeInterceptor"/>  
   </websocket:handshake-interceptors>  
</websocket:handlers>

其中,WebSocketEndPoint和HandShakeInterceptor是自己写的处理类和拦截器。先把这两个类的代码贴出来吧!

package cn.remotejob.websocket;


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.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;


import cn.remotejob.constant.Constant;


public class HandShakeInterceptor extends HttpSessionHandshakeInterceptor {


@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
// 解决The extension [x-webkit-deflate-frame] is not supported问题
if (request.getHeaders().containsKey("Sec-WebSocket-Extensions")) {
request.getHeaders().set("Sec-WebSocket-Extensions",
"permessage-deflate");
}
System.out.println("Before Handshake");

if(request instanceof ServletServerHttpRequest){
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if(session != null){
attributes.put(Constant.WEBSOCKET_SESSIONID, session.getId());
}
}

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);
}


}


package cn.remotejob.websocket;


import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


import javax.annotation.Resource;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.task.TaskExecutor;
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 cn.remotejob.constant.Constant;


public class WebSocketEndPoint extends TextWebSocketHandler {

private static Logger logger = LoggerFactory.getLogger(WebSocketEndPoint.class);

private static final List<WebSocketSession> WEB_SOCKET_SESSIONS = new ArrayList<WebSocketSession>();


@Resource
private TaskExecutor taskExecutor;

@Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
session.sendMessage(message);
// sendAll(message);
}


@Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
logger.info("connect to the websocket success......"+session.getAttributes().get(Constant.WEBSOCKET_SESSIONID));
WEB_SOCKET_SESSIONS.add(session);
session.sendMessage(new TextMessage("Server:connected OK!"));
}


@Override
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
logger.info("websocket connection closed......"+session.getAttributes().get(Constant.WEBSOCKET_SESSIONID));
WEB_SOCKET_SESSIONS.remove(session);
}


@Override
public void afterConnectionClosed(WebSocketSession session,
CloseStatus status) throws Exception {
logger.info("websocket connection closed......"+session.getAttributes().get(Constant.WEBSOCKET_SESSIONID));
WEB_SOCKET_SESSIONS.remove(session);
}

/**
* 给所有在线用户发送消息
* @param message
*/
public void sendAll(final TextMessage message){
for(final WebSocketSession session : WEB_SOCKET_SESSIONS){
taskExecutor.execute(new Runnable() {
@Override
public void run() {
try {
logger.info("The current thread is: "+Thread.currentThread().getId()+"**"+Thread.currentThread().getName());
session.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
});

}
}

/**
* 给某个用户发送消息
* @param sessionId
* @param message
*/
public void sendToUser(final String sessionId,final TextMessage message){
taskExecutor.execute(new Runnable() {
@Override
public void run() {
for(WebSocketSession session : WEB_SOCKET_SESSIONS){
if(sessionId.equals(session.getAttributes().get(Constant.WEBSOCKET_SESSIONID))){
try {
session.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
});
}


}


先看看HandShakeInterceptor拦截器。websocket连接的时候需要先握手,这个拦截器可以在握手前后做一些额外的处理,比如记录日志。但在这里我觉得最有用的还是用来把会话session的id保存到WebSocketSession的atributes里面,这样可以向指定的用户发送消息。

在WebSocketEndPoint类中我用到了线程池执行器taskExecutor,配置如下:

<!--线程池执行器-->  
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">  
   <property name="corePoolSize" value="5"/>
   <property name="maxPoolSize" value="20"/>
   <property name="queueCapacity" value ="100" />
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>


WebSocketEndPoint里面对消息的处理很简单,就是接收到消息后直接返回给客户端,但真实场景通常都是针对接收到的消息做一些业务处理,把业务处理的结果发送给客户端。如果业务处理比较耗时或者并发量比较高,用线程池是很好的选择。

类中还用到了一个集合WEB_SOCKET_SESSIONS,用来保存建立起来的连接。遍历它,可以给所有客户端发送消息。


最后,通过页面来测试一下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Web Socket JavaScript Echo Client</title>
<%
request.setAttribute("wsPath", request.getServerName()+":"+request.getServerPort()+request.getContextPath());
%>
  <script type="text/javascript">
    var ws;
    function init() {
      output = document.getElementById("output");
      send_echo();
    }


    function send_echo() {
    var wsUrl;
   
    if (window.location.protocol == 'http:') {  
    wsUrl = "ws://${wsPath}/ws/websocket.ws";
        } else {  
        wsUrl = "wss://${wsPath}/ws/websocket.ws";
        }
   
    writeToScreen("Connecting to "+wsUrl);
   
      if ('WebSocket' in window) {
     ws = new WebSocket(wsUrl);
      }else if ('MozWebSocket' in window) {
     ws = new MozWebSocket(wsUrl);
      }else {
          alert("您的浏览器版本过低,不支持WebSocket!");
          return;
      }
      
      ws.onopen = function (evt) {
        writeToScreen("Connected !");
      };
      ws.onmessage = function (evt) {
        writeToScreen("Received message: " + evt.data);
        //ws.close();
      };
      ws.onerror = function (evt) {
        writeToScreen('<span style="color: red;">ERROR:</span> '
          + evt.data);
        ws.close();
      };
    }
    function doSend() {
      ws.send(textID.value);
      writeToScreen("Sent message: " + textID.value);
    }
    function writeToScreen(message) {
      var pre = document.createElement("p");
      pre.style.wordWrap = "break-word";
      pre.innerHTML = message;
      output.appendChild(pre);
    }
    window.addEventListener("load", init, false);
  </script>
</head>
<body>
<h1>Echo Server</h1>
<div style="text-align: left;">
  <form action="">
    <input οnclick="doSend()" value="发送socket请求" type="button">
    <input id="textID" name="message" value="Hello World, Web Sockets" type="text">
    <br>
  </form>
</div>
<div id="output"></div>
</body>
</html>


注意:websocket的请求url是根据<url-pattern>/ws/*</url-pattern><!-- 这是websocket的请求 -->和<websocket:mapping path="/websocket.ws" handler="websocket"/>决定的。所以这里的请求url就是/ws/websocket.ws。如果改成如下配置:

<url-pattern>*.ws</url-pattern>

那么请求url就是/websocket.ws了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值