简介(个人理解,有偏差请打脸):Spring4.0以后新增支持的websocket通讯协议,应用层采用stomp协议,消息处理,默认采用内存消息中继,就是将收到和要转发的消息存储在内存中,显然,服务器重启,消息就不存在了。另外支持外接中继组件,如activeMQ/RabbitMQ等消息服务器,产品的话,官网说,为了稳定性和拓展性等各种性推荐使用第三方消息中继(路由)。(中继/路由)就是一个叫法,显得比较专业,说白了就是个消息中间件,再白就是一个消息转发/处理的服务器。
下面先弄第一部分,认识一下webSocket,配置简单易懂(虽然我开始弄也碰一鼻子灰, 慢慢来)。
具体实现:
1. pom.xml(maven,不用maven的下载jar包,引入)
spring-websocket/messaging 4.3.3.Release
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
fastxml 消息转化什么的要用的
fasterxml 2.4.4
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
其他spring依赖,根据需要添加就可以了。
2. WebSocket配置类,用于启用webSocket
package com.websocket.socket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
/**
* websocket 配置类, 采用websocket形式实现的通讯类似于直接使用tcp协议,属于底层协议,spring中应用层
* 采用高层的协议进行长连接通讯,stomp
* @author tomZ
* @date 2016年11月2日
* @desc TODO
*/
@Configuration
@EnableWebMvc
@EnableWebSocket
public class MyWebSocketConfig implements WebSocketConfigurer {
@Autowired
private MyWebSocketHander handler;
@Autowired
private MyHandshakeInterceptor handshakeInterceptor;
/**
* 注册支持的websocket 连接
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry reg) {
/**
* websocket形式连接, client连接
*/
reg.addHandler(handler, "/websocket").addInterceptors(handshakeInterceptor);
/**
* 不支持websocket的,采用sockjs
*/
reg.addHandler(handler, "/sockjs/websocket").addInterceptors(handshakeInterceptor).withSockJS();
}
}
拦截器和处理器类:
package com.websocket.socket;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
/**
* 建立连接,握手拦截器
* @author tomZ
* @date 2016年11月2日
* @desc TODO
*/
@Component
public class MyHandshakeInterceptor implements HandshakeInterceptor {
private static final Logger logger = LoggerFactory.getLogger(MyHandshakeInterceptor.class);
@Override
public void afterHandshake(ServerHttpRequest req, ServerHttpResponse resp, WebSocketHandler handler, Exception e) {
logger.info("after hand shake {}", this.getClass().getName());
}
@Override
public boolean beforeHandshake(ServerHttpRequest req, ServerHttpResponse resp, WebSocketHandler handler,
Map<String, Object> map) throws Exception {
logger.info("before hand shake {}", this.getClass().getName());
/**
* This is a bug,bug fix:The extension [x-webkit-deflate-frame] is not supported
*/
if(req.getHeaders().containsKey("Sec-WebSocket-Extensions")) {
req.getHeaders().set("Sec-WebSocket-Extensions", "permessage-deflate");
}
return true;
}
}
package com.websocket.socket;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import com.beust.jcommander.internal.Maps;
/**
* websocket 处理器
*
* @author tomZ
* @date 2016年11月2日
* @desc TODO
*/
@Component
public class MyWebSocketHander implements WebSocketHandler {
private static final Logger logger = LoggerFactory.getLogger(MyWebSocketHander.class);
private static final Map<String, WebSocketSession> sessions = Maps.newHashMap();
// 连接建立后操作
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("connection established . sessionId {}, remote addr {}", session.getId(),
session.getRemoteAddress());
sessions.put(session.getId(), session);
}
// 处理收到消息操作
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> msg) throws Exception {
logger.info("handler message. sessionId {}, remote addr {}, message {}", session.getId(),
session.getRemoteAddress(), msg.getPayload());
sendMessage(msg);
}
// 消息处理错误操作
@Override
public void handleTransportError(WebSocketSession session, Throwable e) throws Exception {
if (session.isOpen()) {
sessions.remove(session.getId());
session.close();
}
logger.error("handle trainsport error", e);
}
// 是否支持大消息分部分发送
@Override
public boolean supportsPartialMessages() {
return false;
}
// 连接关闭后操作
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
logger.info("connection closed . sessionId {}, remote addr {}", session.getId(), session.getRemoteAddress());
sessions.remove(session.getId());
}
/**
* 发送信息
*
* @param msg
*/
private void sendMessage(WebSocketMessage<?> msg) {
for (Entry<String, WebSocketSession> ses : sessions.entrySet()) {
WebSocketSession s = ses.getValue();
try {
if (s.isOpen()) {
s.sendMessage(msg);
} else {
sessions.remove(s.getId());
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
}
3.上面就是WebSocket基本配置,最后在spring-context.xml (application-context.xml)中扫描上面类所在的包即可。
<context:component-scan base-package="com.websocket.socket" ><!-- base-package 如果多个,用“,”分隔 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
4. jsp页面,websocket.jsp,网上找的页面,自己改了改,有用到bootstrap样式/js,socket.js, jquery
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="ctxStatic" value="${pageContext.request.contextPath}/static"/>
<%
String path = request.getContextPath();
String basePath = request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head lang="en">
<script src="${ctxStatic }/websocket/sockjs-0.3.min.js"></script>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="${ctxStatic }/bootstrap/3.3.0/css/bootstrap.min.css">
<!-- 可选的Bootstrap主题文件(一般不用引入) -->
<link rel="stylesheet" href="${ctxStatic }/bootstrap/3.3.0/css/bootstrap-theme.min.css">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="${ctxStatic}/jquery/jquery-1.9.1.min.js" type="text/javascript"></script>
<!--<script type="text/javascript" src="js/jquery-1.7.2.js"></script>-->
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="${ctxStatic }/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<title>webSocket测试</title>
<script type="text/javascript">
$(document).ready(function() {
var websocket;
if ('WebSocket' in window) {
alert("WebSocket");
websocket = new WebSocket("ws://localhost:18080/项目名称/websocket");
} else if ('MozWebSocket' in window) {
alert("MozWebSocket");
websocket = new MozWebSocket("ws://echo");
} else {
alert("SockJS");
websocket = new SockJS("http://localhost:18080/项目名称/sockjs/websocket");
}
websocket.onopen = function (evnt) {
$("#tou").html("链接服务器成功!")
};
websocket.onmessage = function (evnt) {
$("#msg").html("Received: " + evnt.data);
};
websocket.onerror = function (evnt) {
};
websocket.onclose = function (evnt) {
$("#tou").html("与服务器断开了链接!")
}
$('#send').bind('click', function() {
send();
});
function send(){
if (websocket != null) {
var message = document.getElementById('message').value;
websocket.send(message);
} else {
alert('未与服务器链接.');
}
}
});
</script>
</head>
<body>
<div class="page-header" id="tou">
webSocket即时消息测试
</div>
<div class="well" id="msg">
</div>
<div class="col-lg">
<div class="input-group">
<input type="text" class="form-control" placeholder="发送信息..." id="message">
<span class="input-group-btn">
<button class="btn btn-default" type="button" id="send" >发送</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-6 -->
</body>
</html>
5.请求jsp页面,并发送,自己就能接收到了。http://localhost:18080/项目/socket