一、pom.xml文件依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
二、application.properties配置
server:
address: localhost
port: 8080
spring:
http:
encoding:
force: true
charset: UTF-8
freemarker:
request-context-attribute: request
# prefix: /templates/
suffix: .html
content-type: text/html
enabled: true
cache: false
charset: UTF-8
allow-request-override: false
expose-request-attributes: true
expose-session-attributes: true
expose-spring-macro-helpers: true
三、config配置
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
config添加解析报文格式的配置
package com.hzhl.fastech.config;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.converter.StringHttpMessageConverter;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
public class FastjsonConverter {
@Bean
public HttpMessageConverters customConverters() {
// 定义一个转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 添加fastjson的配置信息 比如 :是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
//fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
List<MediaType> fastMediaTypes = new ArrayList<MediaType>();
// 处理中文乱码问题1
// 处理中文乱码问题2
fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//fastMediaTypes.add(MediaType.valueOf("text/plain;charset=UTF-8"));
//fastMediaTypes.add(MediaType.valueOf("text/html;charset=UTF-8"));
fastConverter.setSupportedMediaTypes(fastMediaTypes);
// 在转换器中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setDefaultCharset(Charset.forName("UTF-8"));
stringConverter.setSupportedMediaTypes(fastMediaTypes);
// 将转换器添加到converters中
return new HttpMessageConverters(stringConverter,fastConverter);
}
}
四、代码
service代码:
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hzhl.fastech.entity.QuestionBank;
import com.hzhl.fastech.entity.TeamScore;
import com.hzhl.fastech.repository.AnsweringRepository;
import com.hzhl.fastech.repository.QuestionBankRepository;
import com.hzhl.fastech.util.SpringUtil;
@Component
@ServerEndpoint("/imserver")
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount = 0;
/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
static List<WebSocketServer> WebSocketServerList = Collections.synchronizedList(new ArrayList<WebSocketServer>());
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userId*/
private String userId="";
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
WebSocketServerList.add(this);
//加入set中
addOnlineCount();
//在线数加1
log.info("用户连接,当前在线人数为:" + getOnlineCount());
try {
sendMessage("连接成功");
} catch (IOException e) {
log.error("用户:"+userId+",网络异常!!!!!!");
} catch (Exception e) {
log.error(e.getMessage());
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
this.session = session;
WebSocketServerList.remove(this);
//从set中删除
subOnlineCount();
log.info("用户退出,当前在线人数为:" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("用户消息:","报文:"+message);
//可以群发消息
//消息保存到数据库、redis
if(StringUtils.isNotBlank(message)){
try {
//解析发送的报文
JSONObject jsonObject = JSON.parseObject(message);
//传送所有用户的websocket
if (WebSocketServerList.size() != 0) {
for (WebSocketServer webSocketServer : WebSocketServerList) {
webSocketServer.session.getBasicRemote().sendText(message);
}
} else{
log.info("当前没有用户在线!");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:","原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 发送自定义消息
* */
public static void sendInfo(String message) throws IOException {
log.info("发送消息到所有客户端,报文:"+message);
if (WebSocketServerList.size() != 0) {
for (WebSocketServer webSocketServer : WebSocketServerList) {
webSocketServer.session.getBasicRemote().sendText(message);
}
} else{
log.info("当前没有用户在线!");
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
controller代码:
package com.hzhl.fastech.controller;
import java.io.IOException;
import java.util.Map;
import javax.annotation.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import com.hzhl.fastech.service.QuestionBankService;
import com.hzhl.fastech.service.WebSocketServer;
import com.hzhl.fastech.vo.Msg;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/question")
public class TestController {
@GetMapping("/index")
public ResponseEntity<String> index(){
return ResponseEntity.ok("请求成功");
}
@GetMapping("/page")
public ModelAndView page(){
return new ModelAndView("websocket");
}
@RequestMapping("/push")
public ResponseEntity<String> pushToWeb(String message) throws IOException {
WebSocketServer.sendInfo(message);
return ResponseEntity.ok("MSG SEND SUCCESS");
}
}
前端页面:在resource/templates目录下创建websocket.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>websocket通讯</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
<script>
var socket;
function openSocket() {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
//等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/01");
var socketUrl="http://10.73.250.202:8080/imserver";
socketUrl=socketUrl.replace("https","ws").replace("http","ws");
console.log(socketUrl);
if(socket!=null){
socket.close();
socket=null;
}
socket = new WebSocket(socketUrl);
//打开事件
socket.onopen = function() {
console.log("websocket已打开");
//socket.send("这是来自客户端的消息" + location.href + new Date());
};
//获得消息事件
socket.onmessage = function(msg) {
console.log(msg.data);
//发现消息进入 开始处理前端触发逻辑
};
//关闭事件
socket.onclose = function() {
console.log("websocket已关闭");
};
//发生了错误事件
socket.onerror = function() {
console.log("websocket发生了错误");
}
}
}
function sendMessage() {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else {
console.log("您的浏览器支持WebSocket");
console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
}
}
</script>
<body>
<p>【userId】:<div><input id="userId" name="userId" type="text" value="10"></div>
<p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="20"></div>
<p>【toUserId】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
<p>【操作】:<div><a onclick="openSocket()">开启socket</a></div>
<p>【操作】:<div><a onclick="sendMessage()">发送消息</a></div>
</body>
</html>
项目路径 + /index
项目路径 + /page
然后访问前端页面就可以了
Springboot打包
由于Spring版本不同,打包过程可能会对代码配置进行测试,websocket可能会报错,这里在测试启动类上@SpringBootTest注解后添加(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)或删除单元测试的包即可
package com.hzhl.fastech;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class AnsweringServiceApplicationTests {
@Test
void contextLoads() {
}
}