Table of Contents
2.3 服务器添加 WebSocket 服务 - NzWebSocketConfig
2.4 WebSocket 握手拦截器 - NzHandShakeInterceptor
2.5 WebSocket 处理器 - NzWebSocketHandler
1. pom.xml - 添加所需jar包
<!-- WebSocket配置开始-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
<!-- WebSocket配置结束-->
2. 创建 websocket 包
2.1 Message 消息类
import java.sql.Timestamp;
public class Message {
/**
* 消息ID
*/
private String messageId;
/**
* 发送者ID
*/
private String fromId;
/**
* 发送者Name
*/
private String fromName;
/**
* 接收者 userSocketSessionMap - ID , 存放在集合中链接 - ID
*/
private String toId;
/**
* 发送的信息
*/
private String messageText;
/**
* 发送的时间
*/
private Timestamp messageDate;
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
public String getFromId() {
return fromId;
}
public void setFromId(String fromId) {
this.fromId = fromId;
}
public String getFromName() {
return fromName;
}
public void setFromName(String fromName) {
this.fromName = fromName;
}
public String getToId() {
return toId;
}
public void setToId(String toId) {
this.toId = toId;
}
public String getMessageText() {
return messageText;
}
public void setMessageText(String messageText) {
this.messageText = messageText;
}
public Timestamp getMessageDate() {
return messageDate;
}
public void setMessageDate(Timestamp messageDate) {
this.messageDate = messageDate;
}
@Override
public String toString() {
return "Message{" +
"messageId='" + messageId + '\'' +
", fromId='" + fromId + '\'' +
", fromName='" + fromName + '\'' +
", toId='" + toId + '\'' +
", messageText='" + messageText + '\'' +
", messageDate=" + messageDate +
'}';
}
}
2.2 User 用户类
public class User {
/**
* 用户ID
*/
private String userId;
/**
* 用户姓名
*/
private String userName;
/**
* 用户聊天记录
*/
private String userText;
/**
* 用户IP地址
*/
private String userIp;
/**
* 用户最后发送时间
*/
private String userSendTime;
public User(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserText() {
return userText;
}
public void setUserText(String userText) {
this.userText = userText;
}
public String getUserIp() {
return userIp;
}
public void setUserIp(String userIp) {
this.userIp = userIp;
}
public String getUserSendTime() {
return userSendTime;
}
public void setUserSendTime(String userSendTime) {
this.userSendTime = userSendTime;
}
}
2.3 服务器添加 WebSocket 服务 - NzWebSocketConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
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;
import javax.servlet.http.HttpSession;
@Component
@EnableWebSocket
@Controller
public class NzWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
/**
* 当WebSocketHandler类被加载时就会创建该Integer,随类而生
* 测试用的,用于分辨
*/
public static Integer count = 0;
/**
* 跳转到聊天View
* @param session
* @return
*/
@RequestMapping("conSocket")
public String conSocket(HttpSession session){
count++;
session.setAttribute("user", new User("NZ" + count));
return "nz/webSocketJsp";
}
@Autowired
NzWebSocketHandler handler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
//添加websocket处理器,添加握手拦截器
webSocketHandlerRegistry.addHandler(handler, "/websocket").addInterceptors(new NzHandShakeInterceptor());
}
}
2.4 WebSocket 握手拦截器 - NzHandShakeInterceptor
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;
import javax.servlet.http.HttpSession;
import java.util.Map;
public class NzHandShakeInterceptor implements HandshakeInterceptor {
/**
* 握手前的处理
* @param serverHttpRequest, serverHttpResponse, webSocketHandler, map
* @return boolean
*/
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
System.out.println("Websocket:用户[ID:" + ((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getSession(false).getAttribute("user") + "]已经建立连接");
if (serverHttpRequest instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) serverHttpRequest;
HttpSession session = servletRequest.getServletRequest().getSession(false);
// 标记用户
User user = (User) session.getAttribute("user");
if (user != null) {
// 为服务器创建WebSocketSession做准备
map.put("userId", user.getUserId());
System.out.println("用户id:" + user.getUserId() + " 被加入");
} else {
System.out.println("user为空");
return false;
}
}
return true;
}
/**
* 握手后的处理
* @param serverHttpRequest, serverHttpResponse, webSocketHandler, excption
*/
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception excption) {
}
}
2.5 WebSocket 处理器 - NzWebSocketHandler
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Component
public class NzWebSocketHandler implements WebSocketHandler {
// /**
// * 数据访问层
// */
// @Autowired
// private SendMsgService sendMsgService;
/**
* 当WebSocketHandler类被加载时就会创建该Map,随类而生
*/
public static final Map<String, WebSocketSession> userSocketSessionMap;
static {
userSocketSessionMap = new HashMap<>();
}
/**
* 握手实现连接后的处理
* @param webSocketSession
*/
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) {
String userId = (String) webSocketSession.getAttributes().get("userId");
if (userSocketSessionMap.get(userId) == null) {
userSocketSessionMap.put(userId, webSocketSession);
}
}
/**
* 发送信息前的处理
* @param webSocketSession
* @param webSocketMessage
* @throws Exception
*/
@Override
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
if (webSocketMessage.getPayloadLength() == 0) {
return;
}
//得到Socket通道中的数据并转化为Message对象
Message msg = new Gson().fromJson(webSocketMessage.getPayload().toString(), Message.class);
Timestamp now = new Timestamp(System.currentTimeMillis());
msg.setMessageDate(now);
// System.err.println("size = " + userSocketSessionMap.size());
// for (Map.Entry map : userSocketSessionMap.entrySet()) {
// System.err.println(map.getKey() + " val= " + map.getValue());
// }
// System.err.println("---------------------------------------------------");
//将信息保存至数据库
//sendMsgService.addMessage(msg.getFromId(), msg.getFromName(), msg.getToId(), msg.getMessageText(), msg.getMessageDate());
//发送Socket信息
sendMessageToUser(msg.getToId() + "", new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(msg)));
}
/**
* 握手传输错误后的处理
* @param webSocketSession
* @param throwable
* @throws Exception
*/
@Override
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
}
/**
* 连接关闭前的处理
* 在此刷新页面就相当于断开WebSocket连接,原本在静态变量userSocketSessionMap中的
* WebSocketSession会变成关闭状态(close),但是刷新后的第二次连接服务器创建的
* 新WebSocketSession(open状态)又不会加入到userSocketSessionMap中,所以这样就无法发送消息
* 因此应当在关闭连接这个切面增加去除userSocketSessionMap中当前处于close状态的WebSocketSession,
* 让新创建的WebSocketSession(open状态)可以加入到userSocketSessionMap中
*
* @param webSocketSession
* @param closeStatus
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
System.out.println("WebSocket:" + webSocketSession.getAttributes().get("userId") + " close connection");
Iterator<Map.Entry<String, WebSocketSession>> iterator = userSocketSessionMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, WebSocketSession> entry = iterator.next();
if (entry.getValue().getAttributes().get("userId") == webSocketSession.getAttributes().get("userId")) {
userSocketSessionMap.remove(webSocketSession.getAttributes().get("userId"));
System.out.println("WebSocket in staticMap:" + webSocketSession.getAttributes().get("userId") + " removed");
}
}
}
/**
* 是否支持局部的消息
* @return
*/
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 发送信息的实现
* @param userId, message
* @throws IOException
*/
public void sendMessageToUser(String userId, TextMessage message) throws IOException {
WebSocketSession session = userSocketSessionMap.get(userId);
if (session != null && session.isOpen()) {
session.sendMessage(message);
}
}
}
3. webSocketJsp - 创建一个页面
<%@ page language="java" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>WebSocket</title>
</head>
<body>
<div>
<div id="connect-container">
<div>
<button id="connect" onclick="connect('/ListMap/websocket');">Connect</button>
</div>
<div>
<textarea id="message" style="width: 350px">Here is a message!</textarea>
</div>
<div>
<button id="send" onclick="send();">Send message</button>
</div>
</div>
<div id="console"></div>
</div>
</body>
<script type="text/javascript" src="${pageContext.request.contextPath }/statics/layui/jquery-2.1.1.js"></script>
<script type="text/javascript">
var webSocket = null;
var url = null;
/**
* 创建连接
*/
function connect(urlPath) {
/**
* 判断协议 - http or https
*/
url = isUrl(urlPath);
/**
* 显示信息
*/
console.log("链接url为:" + url);
/**
* 建立连接
*/
webSocket = new WebSocket(url);
/**
* 连接成功
*/
webSocket.onopen = function () {
console.log('Info: 连接成功.');
};
/**
* 连接失败
*/
webSocket.onerror = function () {
console.log('Info: 连接失败.');
};
/**
* 连接断开
*/
webSocket.onclose = function (event) {
console.log('Info: 连接断开.');
console.log(event.data);
};
/**
* 来自服务器的消息
*/
webSocket.onmessage = function (event) {
var data = JSON.parse(event.data);
showText(data['messageDate'] + '<br/>Info: 来自服务器的消息. ' + data['messageText']);
};
}
/**
* 发送消息
*/
function send() {
// 判断是否已连接
if (webSocket != null) {
// 新建data对象,并规定属性名与相应的值
var data = {};
// 发送者ID
data['fromId'] = 'NZ-ID-' + new Date().getTime();
// 发送者Name
data['fromName'] = 'NZ-NAME-' + new Date().getTime();
// 接收者ID - userSocketSessionMap - key
// 注意 - NZ1 代表是第一个连接人的key - 测试
// 消息发送的也是给第一个连接者
data['toId'] = 'NZ1';
// 发送的消息
data['messageText'] = document.getElementById('message').value;
// 将对象封装成JSON后发送至服务器
webSocket.send(JSON.stringify(data));
// 显示消息
console.log('Sent: ' + data['messageText']);
} else {
console.log('Send: 连接未建立,请连接。');
}
}
/**
* 显示文本
* @param message
*/
function showText(message) {
var console = $('#console');
$(console).html($(console).html() + "<br/>" + message);
}
/**
* 判断协议 - http or https
*/
function isUrl(urlPath) {
if (window.location.protocol == 'http:') {
return 'ws://' + window.location.host + urlPath;
} else {
return 'wss://' + window.location.host + urlPath;
}
}
</script>
</html>
4. 结果图
参考链接: https://blog.csdn.net/qq_33290787/article/details/52245041