项目结构
pom 文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
SocketConfig装配
package com.winmine.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class SocketConfig {
/**
* ServerEndpointExporter 作用
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
SocketServer核心服务层
package com.winmine.websocket.service;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@ServerEndpoint("/robot/webSocket/{sid}")//前端页面请求的uri地址,其中{sid}是动态参数,通过@PathParam(value = "sid") 获取
@Component
public class SocketServer {
//记录当前在线连接数。需要保证线程安全
private static AtomicInteger onlineNum = new AtomicInteger();
//线程安全Set,存放每个客户端对应的WebSocketServer对象。
private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();
//发送消息
public void sendMessage(Session session, String message) throws IOException {
if (session != null) {
synchronized (session) {
session.getBasicRemote().sendText(message);
}
}
}
//建立连接成功调用
@OnOpen
public void onOpen(Session session, @PathParam(value = "sid") String userName) {
sessionMap.put(userName, session);
onlineNum.incrementAndGet();
System.out.println(userName + "加入webSocket!当前人数为" + onlineNum);
try {
sendMessage(session, "欢迎" + userName + "加入连接!");
} catch (IOException e) {
e.printStackTrace();
}
}
//关闭连接时调用
@OnClose
public void onClose(@PathParam(value = "sid") String userName) {
sessionMap.remove(userName);
onlineNum.decrementAndGet();
System.out.println(userName + "断开连接!当前人数为" + onlineNum);
}
//收到客户端信息并发送到另一个客户端
@OnMessage
public void onMessage(@PathParam(value = "sid") String userName, String message) throws IOException {
JSONObject req = JSONObject.parseObject(message);
String toUserId = req.getString("toUserId");
String contentText = req.getString("contentText");
Session session = sessionMap.get(toUserId);
System.out.println(userName + " -> " + toUserId + "\n" + contentText);
try {
sendMessage(session, userName + "对" + toUserId + "说:" + contentText);
} catch (Exception e) {
System.out.println("消息发送异常"+e.getMessage());
}
}
//异常调用
@OnError
public void onError(Session session, Throwable throwable) {
System.out.println("发生错误");
throwable.printStackTrace();
}
}
SocketController控制层
package com.winmine.websocket.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class SocketController {
//web页面入口
@GetMapping("/index")
public ModelAndView socket() {
return new ModelAndView("/robot/webSocket");//访问/resources/templates下的资源路径
}
}
Application入口
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
webSocket.html 前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket</title></head>
<body>
<p>【当前用户id】:
<div><input id="userId" name="userId" type="text" value="1"></div>
<p>【推送消息用户id】:
<div><input id="toUserId" name="toUserId" type="text" value="2"></div>
<p>【推送内容】:
<div><input id="contentText" name="contentText" type="text" value="默认内容"></div>
<p>操作:
<div>
<button type="button" onclick="openSocket()">开启socket</button>
</div>
<p>【操作】:
<div>
<button type="button" onclick="sendMessage()">发送消息</button>
</div>
<p>--------------------分隔符--------------------</p>
<div id="text">【响应】</div>
</body>
<script type="text/javascript">
var socket;
function openSocket() {
if (typeof (WebSocket) == "undefined") {
var serverMsg = "浏览器不支持WebSocket"
document.getElementById("text").innerHTML = serverMsg;
} else {
var serverMsg = "浏览器支持WebSocket"
document.getElementById("text").innerHTML = serverMsg;
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
var userId = document.getElementById('userId').value;
var socketUrl = "ws://" + window.location.host +":8080/robot/webSocket/" + userId;//访问SocketServer,并传输当前id
if (socket != null) {
socket.close();
socket = null;
}
socket = new WebSocket(socketUrl);
socket.onopen = function () {//websocket已打开
socket.send('{"toUserId":"' + userId + '","contentText":"加入客户端' + location.href + DateUtil.now() + '"}');
};
//获得消息事件
socket.onmessage = function (msg) {
var serverMsg = "收到服务端信息:" + msg.data;
document.getElementById("text").innerHTML = serverMsg;
};
socket.onclose = function () {//WebSocket关闭事件
};
socket.onerror = function () {//WebSocket发生了错误事件
}
}
}
function sendMessage() {
if (typeof (WebSocket) == "undefined") {//您的浏览器不支持WebSocket
} else {
var toUserId = document.getElementById('toUserId').value;
var contentText = document.getElementById('contentText').value;
var msg = '{"toUserId":"' + toUserId + '","contentText":"' + contentText + '"}';
socket.send(msg);
}
}
</script>
</html>
访问地址 http://127.0.0.1:8080/index
操作界面