SpringBoot系列-Websocket 实时聊天
实现webSocket所需的环境
本文中demo前端需要引入的JS(版本可参考github上项目Demo,本文根据-关卫明 github项目所写:github项目地址
jquery.js
socket.io.js
socket.io.js地址
所需引入的jar包
<dependency>
<groupId>com.corundumstudio.socketio</groupId>
<artifactId>netty-socketio</artifactId>
<version>1.7.18</version>
</dependency>
前端代码
前端比较简单就不多说了,直接上代码
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>NETTY SOCKET.IO DEMO</title>
<base>
<script src="js/jquery.js"></script>
<script src="js/socket/socket.io.js"></script>
<style>
body {
padding: 20px;
}
#console {
height: 450px;
overflow: auto;
}
.username-msg {
color: orange;
}
.connect-msg {
color: green;
}
.disconnect-msg {
color: red;
}
</style>
</head>
<body>
<h1>湿度:<span id="data"></span></h1>
<h1>更新时间:<span id="current_time"></span></h1>
<h1>状态:<span id="status"></span></h1>
</body>
<script type="text/javascript">
const socket = io('http://127.0.0.1:8888', {
query: {
'token': '123'
},
extraHeaders: {
"token": "666"
}
});
socket.on('connect', () => {
$("#status").html("连接成功")
})
socket.on('server_event', data => {
$("#data").html(data.msg);
$("#current_time").html(data.date);
});
socket.on('disconnect', () => {
$("#status").html("已下线")
})
</script>
</html>
后端代码
-
在pom.xml文件中引入 netty-socketio包。上文有具体版本号
-
yml文件的配置
server: port: 8080 ws: port: 8888 title: arduino服务器 host: 0.0.0.0 boss-count: 1 work-count: 100 allow-custom-requests: true upgrade-timeout: 10000 ping-timeout: 60000 ping-interval: 25000
-
配置文件AppProperties 配置@ConfigurationProperties(“ws”)注解后会自动找到yml文件中以ws开头的配置项注入
package com.coding.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author guanweiming */ @Data @ConfigurationProperties("ws") public class AppProperties { /** * title */ private String title; /** * host */ private String host; /** * port */ private Integer port; /** * bossCount */ private int bossCount; /** * workCount */ private int workCount; /** * allowCustomRequests */ private boolean allowCustomRequests; /** * upgradeTimeout */ private int upgradeTimeout; /** * pingTimeout */ private int pingTimeout; /** * pingInterval */ private int pingInterval; }
-
class AppConfiguration中 @EnableConfigurationProperties将类AppProperties 注册为容器的bean
package com.coding.config; import com.corundumstudio.socketio.SocketConfig; import com.corundumstudio.socketio.SocketIOServer; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author guanweiming */ @Slf4j @Configuration @EnableConfigurationProperties({ AppProperties.class, }) public class AppConfiguration { @Bean public SocketIOServer socketIoServer(AppProperties appProperties) { SocketConfig socketConfig = new SocketConfig(); socketConfig.setTcpNoDelay(true); socketConfig.setSoLinger(0); com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration(); config.setSocketConfig(socketConfig); config.setHostname(appProperties.getHost()); config.setPort(appProperties.getPort()); config.setBossThreads(appProperties.getBossCount()); config.setWorkerThreads(appProperties.getWorkCount()); config.setAllowCustomRequests(appProperties.isAllowCustomRequests()); config.setUpgradeTimeout(appProperties.getUpgradeTimeout()); config.setPingTimeout(appProperties.getPingTimeout()); config.setPingInterval(appProperties.getPingInterval()); return new SocketIOServer(config); } }
- MsgSendService 记录了socket通讯相关方法
package com.coding.service; import com.corundumstudio.socketio.SocketIOClient; import com.corundumstudio.socketio.SocketIOServer; import io.netty.handler.codec.http.HttpHeaders; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @Slf4j @Service @RequiredArgsConstructor public class MsgSendService { private static final Map<UUID, SocketIOClient> CLIENT_MAP = new ConcurrentHashMap<>(); private final SocketIOServer socketIOServer; /** * 启动的时候会被调用一次 */ @PostConstruct private void autoStart() { log.info("start ws"); socketIOServer.addConnectListener(client -> { String token = getClientToken(client, "token"); if (checkToken(token)) { log.info("check success"); CLIENT_MAP.put(client.getSessionId(), client); } else { client.disconnect(); } }); socketIOServer.addDisconnectListener(client -> { CLIENT_MAP.remove(client.getSessionId()); client.disconnect(); log.info("移除client:{}", client.getSessionId()); }); socketIOServer.start(); log.info("start finish"); } private boolean checkToken(String token) { log.info("检查token是否有效:{}", token); return true; } private String getClientToken(SocketIOClient client, String key) { HttpHeaders httpHeaders = client.getHandshakeData().getHttpHeaders(); return httpHeaders.get(key); } @PreDestroy private void onDestroy() { if (socketIOServer != null) { socketIOServer.stop(); } } public int sendMsg(Object demo) { CLIENT_MAP.forEach((key, value) -> { value.sendEvent("server_event", demo); log.info("发送数据成功:{}", key); }); return CLIENT_MAP.size(); } }
- 配合接口的使用 (其中@Api开头的注解是属于swagger.ui的注解,不需要可以去除)
@ApiOperation("发送消息") @GetMapping("send") public String send(String msg) { Map<String,Object> map=new HashMap<>(); map.put("msg",msg); map.put("date", LocalDateTime.now().toString()); int size = msgSendService.sendMsg(map); return "发送成功" + size; } }