这篇文章将介绍如何构建一个简单的WebSocket消息推送Demo
使用eclipse建立maven项目后引入相关的依赖jar包,如下:
我们需要编写三个WebSocket的处理类,这里是利用了Spring WebSocket模块,三个类分别是WebSocket处理类SocketHandler、WebSocket配置类WebSocketConfig以及WebSocket拦截器WebSocketInterceptor。
在开始编写之前记得在Spring配置文件中添加Spring注解支持:
WebSocketConfig.java
package com.accenture.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.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;
/**
* @desp websocket配置
*
*/
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{
@Autowired
private SocketHandler socketHandler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//注册处理拦截器,拦截url为socketServer的请求
registry.addHandler(socketHandler, "/socketServer").addInterceptors(new WebSocketInterceptor());
//注册SockJs的处理拦截器,拦截url为/sockjs/socketServer的请求
registry.addHandler(socketHandler, "/sockjs/socketServer").addInterceptors(new WebSocketInterceptor()).withSockJS();
}
}
如上所示,在WebSocketConfig.java中,我们为SocketHandler注册了两个url请求,并应用了我们所定义的WebSocketInterceptor()拦截器。
WebSocketInterceptor.java
package com.accenture.socket;
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;
/**
* @desp websocket拦截器
*
*/
public class WebSocketInterceptor implements HandshakeInterceptor{
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler handler, Exception exception) {
}
/**
* @desp 将HttpSession中对象放入WebSocketSession中
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler handler,
Map
if(request instanceof ServerHttpRequest){
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession();
if(session!=null){
//区分socket连接以定向发送消息
map.put("user", session.getAttribute("user"));
}
}
return true;
}
}
如上所示,在执行客户端服务器端握手之前,也就是在beforeHandshake()方法中,我们将HttpSession中我们登录后存储的对象放到WebSocketSession中,以此实现定向发送消息。
SocketHandler.java
package com.accenture.socket;
import java.io.IOException;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
* @desp Socket处理类
* @author [email protected]
*
*/
@Service
public class SocketHandler implements WebSocketHandler{
private static final Logger logger;
private static final ArrayList
static{
users = new ArrayList
logger = LoggerFactory.getLogger(SocketHandler.class);
}
@Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
logger.info("成功建立socket连接");
users.add(session);
String username = session.getAttributes().get("user").toString();
if(username!=null){
session.sendMessage(new TextMessage("我们已经成功建立socket通信了"));
}
}
@Override
public void handleMessage(WebSocketSession arg0, WebSocketMessage> arg1)
throws Exception {
// TODO Auto-generated method stub
}
@Override
public void handleTransportError(WebSocketSession session, Throwable error)
throws Exception {
if(session.isOpen()){
session.close();
}
logger.error("连接出现错误:"+error.toString());
users.remove(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1)
throws Exception {
logger.debug("连接已关闭");
users.remove(session);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 给所有在线用户发送消息
*
* @param message
*/
public void sendMessageToUsers(TextMessage message) {
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 给某个用户发送消息
*
* @param userName
* @param message
*/
public void sendMessageToUser(String userName, TextMessage message) {
for (WebSocketSession user : users) {
if (user.getAttributes().get("user").equals(userName)) {
try {
if (user.isOpen()) {
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
}
SocketController.java
package com.accenture.controller;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestMethod;
import org.springframework.web.socket.TextMessage;
import com.accenture.socket.SocketHandler;
/**
* @desp Socket控制器
* @author [email protected]
* @date 2016-5-6
*
*/
@Controller
public class SocketController{
private static final Logger logger = LoggerFactory.getLogger(SocketController.class);
@Autowired
private SocketHandler socketHandler;
@RequestMapping(value="/login")
public String login(HttpSession session){
logger.info("用户登录了建立连接啦");
session.setAttribute("user", "liulichao");
return "home";
}
@RequestMapping(value = "/message", method = RequestMethod.GET)
public String sendMessage(){
socketHandler.sendMessageToUser("liulichao", new TextMessage("这是一条测试的消息"));
return "message";
}
}
如上,在SocketController中我们定义了两个请求处理方法,首先执行login()后再session中存入user对象模拟用户已登录,而sendMessage()方法则是调用了sendMessageToUser()实现向某一个用户推送消息。
整个包结构如下
同时,在web.xml中需要所有servlet与filter中需要加入异步支持:
前端页面实现
模拟登录页面home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
String wsPath = "ws://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
Hello world! This is a WebSocket demo!
如上所示,我们访问login模拟登录后之后将跳到home.jsp,在页面中通过sockjs与服务器端我们注册的WebSocket访问接口实现握手建立socket连接。
推送消息页面message.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
在访问/message过后将调用消息发送的函数实现后端消息向前端的推送。
首先,我们访问login函数,将跳至home.jsp视图,出现如下的页面:
在这一步中,我们已经在后台模拟登录,session中保存了user的对象,紧接着,我们再在新页面中访问message函数:
这时候我们再回去看home.jsp的页面:
可以看到,页面实现了更新,同时在socketServer握手请求之后并没有发生http的请求,同时我们可以在console中看到打印出来的通信的数据:
至此,我们成功实现了服务器端向客户端的消息推送。