在项目中,常规都是前端向后端发送请求后,才能获取到后端的数据。但是在一些及时消息的处理上,这样的处理效率有些捉襟见肘;在以往获得即时数据时,比较low的方案就是ajax轮询查询;或者可以使用socket的长连接;但是这些在实际的操作上都比较消耗资源;
而websocket在这方面有效的解决这个问题--WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端,客户端接收到消息可即时对消息进行处理
一些三方推送平台也提供了更为完善的消息推送技术如:GoEasy
以下是websocket整合Spring的实际运用,注释详细自行参考,案例是用户消息推送
1:实现WebSocketConfigure配置自己的socket链接,前台参考此路径可建立链接
package com.tanli.ssm.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
/**
* 提供配置自己的websocekt类即请求路径
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
MyHandler myHandler = new MyHandler();
registry.addHandler(myHandler(),"/dunHandler").addInterceptors(new WebSocketInterceptor());
}
/**
* 向spring容器注册javabean由spring容器来管理
* @return
*/
@Bean
public WebSocketHandler myHandler(){
return new MyHandler();
}
}
2:t建立链接时,websocketInteceptor对封装在WebsocketSession的map进行处理,map存储当前登录的用户id,以便在handler处理类获取发送消息
package com.tanli.ssm.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.HandshakeInterceptor;
/**
* WebSocket链接时的拦截器
* @author 01
*/
public class WebSocketInterceptor implements HandshakeInterceptor{
/**
* 当客户端与服务器端握手之前之前执行的方法
* 取出当前存在session的用户信息将dunId,封装到WebSocket对象中的map中;
* 由Handler处理器中获取id
* @return
*/
/*@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
Map<String, Object> attribute) throws Exception {
//将增强的request转换httpservletRequest
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
HttpSession session = serverHttpRequest.getServletRequest().getSession();
if (session != null) {
attribute.put("dunId", session.getAttribute("dunId"));
}
}
//放行
return true;
}*/
//测试类
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
Map<String, Object> attribute) throws Exception {
//将增强的request转换httpservletRequest
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
HttpSession session = serverHttpRequest.getServletRequest().getSession();
/* Dun dun = new Dun();
dun.setId(12345);
session.setAttribute("dunId", 12345);
if (session != null) {
attribute.put("dunId", 12345);
}*/
}
//放行
return true;
}
/**
* 与服务器websoket建立握手之后执行的方法
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Exception exception) {
}
}
3 WebSocketHandler处理类,不同事件触发不同方法,主要业务处理类
package com.tanli.ssm.websocket;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
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;
/**
* 处理类:Handler--建立连接,发送消息,关闭连接自动执行
* @author 01
*
*/
public class MyHandler extends TextWebSocketHandler{
//在线用户列表
private static final Map<Integer, WebSocketSession> users;
private static final String DUN_ID = "dunId"; //用户标识
//类加载初始化一个map集合,存放用户的websocket对象
static{
users = new HashMap<Integer, WebSocketSession>();
}
/**
* 获取用户标识,获取websocekt对象的map集合
* @param session
* @return
*/
private Integer getClientId(WebSocketSession session) {
try {
//获取存入websocket的userid
Integer clientId = (Integer) session.getAttributes().get(DUN_ID);
return clientId;
} catch (Exception e) {
return null;
}
}
/**
* 成功建立连接触发的方法,
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//取出拦截器放入的dunID,为当前的websoket绑定用户到map
Integer userId = getClientId(session);
System.out.println(userId+":链接获得id");
if (userId != null) {
users.put(userId, session);
session.sendMessage(new TextMessage("成功建立socket连接"));
System.out.println(userId);
System.out.println(session);
}
}
/**
* 当接收到客户端浏览器后接收的方法
*/
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
System.out.println(message.getPayload());
WebSocketMessage message1 = new TextMessage("server:"+message);
try {
session.sendMessage(message1);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送信息给指定用户
* @param clientId
* @param message
* @return
* map中根据用户的id获取对应得websoket,发送信息
*/
public boolean sendMessageToUser(Integer dunId, TextMessage message) {
//获取当前的
if (users.get(dunId) == null) return false;
WebSocketSession session = users.get(dunId);
System.out.println("sendMessage:" + session);
if (!session.isOpen()) return false;
try {
session.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 广播信息(发送给所有人)
* @param message
* @return
* 遍历出map中所有的websocket发送在线消息
*/
public boolean sendMessageToAllUsers(TextMessage message) {
boolean allSendSuccess = true;
Set<Integer> dunIds = users.keySet();
WebSocketSession session = null;
for (Integer dunId : dunIds) {
try {
session = users.get(dunId);
if (session.isOpen()) {
session.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
allSendSuccess = false;
}
}
return allSendSuccess;
}
/**
* 当链接发生异常后触发的方法,关闭出错会话的连接,和删除在Map集合中的记录
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
//判断当前的链接是否在继续,关闭连接
if (session.isOpen()) {
session.close();
}
System.out.println("连接出错");
users.remove(getClientId(session));
}
/**
* 当链接关闭后触发的方法,连接已关闭,移除在Map集合中的记录。
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("连接已关闭:" + status); //当前的状态码,并删除存储在map中的websocket的链接对象
users.remove(getClientId(session));
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
前端链接以及消息发送代码,包含socket关闭,开启,以及接受消息各种事件触发
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>这是一个jsp的测试类</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"></script>
<script type="text/javascript">
var ws =
$(function(){
ws = new WebSocket("ws://localhost:8080/dun/dunHandler");
ws.onopen = function () {
alert("已经链接")
//ws.send("{}");
}
ws.onclose = function () {
console.log("onclose");
}
ws.onmessage = function (msg) {
// alert(msg.data+"这是接收到的内容");
$("#h1").append("<h4 style='color: royalblue;' >"+msg.data+"</h4>");
}
});
function sendMessage(){
var messageText = $('#messageText').val();
$.post('${pageContext.request.contextPath}/sendMessage',{'messageText':messageText},function(data){
//alert(data.message);
})
}
</script>
</head>
<body>
<div align="center" style="width: 100%;height: 100%;background: moccasin;border: 3px solid darkolivegreen;" >
<h1 align="center">欢迎来到websoket测试页面</h1>
<div align="center" style="width: 600px;height: 400px;border: 2px solid aquamarine;">
<div style="width: 100%;height: 10%;border: 2px solid gray;">
<font style="color: blue;size: a3;">聊天窗口</font>
</div>
<div style="width:100%;height:78%;color: blanchedalmond;border: darkgoldenrod solid seagreen;">
<input type="text" id="message" value="输入的内容" />
<textarea id="description" name="description"></textarea>
<span id="h1" style="color: royalblue;" ><h4 >这是什么</h4></span>
</div>
<div style="width: 100%;height: 10%;border: 2px solid saddlebrown;">
<input type="text" name="messageText" id="messageText" />请输入内容
<input type="button" value="sendBtn" οnclick="sendMessage()" />
</div>
</div>
</div>
</body>
</html>
在controller层业务处理调用,向前端推送消息操作
package com.tanli.ssm.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.TextMessage;
import com.tanli.ssm.pojo.Message;
import com.tanli.ssm.websocket.MyHandler;
@Controller
public class SocketController {
//注入发送消息的Handler类
private MyHandler myHandler =new MyHandler();
@RequestMapping("/sendMessage")
@ResponseBody
public Message sendMessage(String messageText){
TextMessage textMessage = new TextMessage(messageText);
boolean hasSend = myHandler.sendMessageToUser(12345, textMessage);
System.out.println(hasSend);
Message message = new Message();
if (hasSend) {
message.setStatus(1);
message.setMessage("成功接收");
}else{
message.setStatus(0);
message.setMessage("接收失败");
}
return message;
}
}