1.基于java注解实现websocket服务器端
1.1需要的类
1.1.1服务终端类
用java注解来监听连接@ServerEndpoint、连接成功@OnOpen、连接失败@OnClose、收到消息等状态@OnMessage
1.1.2配置类
把spring中的ServerEndpointExporter对象注入进来
2.1代码示例
2.1.1 maven配置
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.heima</groupId> <artifactId>ws-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.7.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.22</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>2.7.14</version> </dependency> </dependencies></project>
2.1.2 WsServerEndpoint类
package com.heima;import lombok.extern.slf4j.Slf4j;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.HashMap;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/*** * 监听websocket地址 /myWs */@ServerEndpoint("/myWs")@Component@Slf4j@EnableSchedulingpublic class WsServerEndpoint { static Map<String,Session> map = new ConcurrentHashMap<String,Session>(); /*** * 连接建立时执行的操作 * @param session */ @OnOpen public void onOpen(Session session) { map.put(session.getId(),session); log.info("websocket is open"); } /*** * 收到客户端消息执行的操作 * @param text */ @OnMessage public String OnMessage(String text) { log.info("收到了一条信息"+text); return "已收到你的信息" ; } /*** * 连接关闭时执行的操作 * @param session */ @OnClose public void OnClose(Session session) { map.remove(session.getId()); log.info("连接关闭时执行的操作"); } /*** * 向客户端发送信息 */ @Scheduled(fixedRate = 2000) public void sendMsg() throws IOException { for (String key : map.keySet()) { map.get(key).getBasicRemote().sendText("你好,你好"); } }}
2.1.3 WebSocketConfig
package com.heima;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configurationpublic class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }}
2.1.3 前端测试代码
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>wsClient</title></head><body><script> // 创建websocket let ws = new WebSocket("ws://localhost:8080/myWs") //向服务器发送hello ws.onopen=function (){ ws.send("hello") } //监听数据ws://localhost:8080/myWs ws.onmessage=function (message){ console.log(message.data) }</script></body></html>
2.1.4测试结果
2.1.4.1 当打开浏览器时
2.1.4.2 当关闭浏览器时
2.1.4.3 当刷新浏览器的时候
2.基于spring提供的类和接口刷新websocket服务器端
2.1:HttpSessionHandShakeInter 握手拦截器
package com.spring;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Configuration;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.stereotype.Component;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;import java.util.Map;/*** * 握手拦截器 */@Component@Slf4jpublic class MyWsInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { log.info(request.getRemoteAddress().toString()+"开始握手"); return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { log.info(request.getRemoteAddress().toString()+"完成握手"); super.afterHandshake(request, response, wsHandler, ex); }}
2.2 MyWsHandler 主处理程序
sessionbean封装类
import lombok.AllArgsConstructor;import lombok.Data;import org.springframework.web.socket.WebSocketSession;@Data@AllArgsConstructorpublic class SessionBean { private WebSocketSession webSocketSession; private Integer clientId;}
主处理程序
package com.spring;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.web.servlet.server.Session;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;import org.springframework.web.socket.CloseStatus;import org.springframework.web.socket.TextMessage;import org.springframework.web.socket.WebSocketSession;import org.springframework.web.socket.handler.AbstractWebSocketHandler;import java.io.IOException;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.atomic.AtomicInteger;/*** * webSocket 主处理程序 */@Component@Slf4j@EnableSchedulingpublic class MyWsHandler extends AbstractWebSocketHandler { //map有并发线程问题 所以用ConcurrentHashMap private static Map<String, SessionBean> map ; //id有并发问题 所以用Integer的安全类型 private static AtomicInteger clientIdMaker; static { map = new ConcurrentHashMap<>(); clientIdMaker=new AtomicInteger(0); } //连接建立 @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); //将session 进一步封装 id采用的是自增 SessionBean sessionBean = new SessionBean(session, clientIdMaker.getAndIncrement()); map.put(session.getId(),sessionBean); log.info(map.get(session.getId()).getClientId()+"建立了连接"); } //收到消息 @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); log.info(map.get(session.getId()).getClientId()+":"+message.getPayload()); } //传输异常 @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { super.handleTransportError(session, exception); if (session.isOpen()) { session.close(); } map.remove(session.getId()); } //连接关闭 @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); log.info(map.get(session.getId()).getClientId()+"关闭连接"); } /*** * 向客户端发送信息 */ @Scheduled(fixedRate = 2000) public void sendMsg() throws IOException { for (String key : map.keySet()) { map.get(key).getWebSocketSession().sendMessage(new TextMessage("hello," + "spring socket")); } }}
2.3 WebSocketConfigurer 注册拦截器和主处理程序以及监听路径
package com.spring;import org.springframework.context.annotation.Configuration;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.annotation.Resource;@Configuration@EnableWebSocketpublic class MyWsConfig implements WebSocketConfigurer { @Resource private MyWsHandler wsHandler; @Resource private MyWsInterceptor wsInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(wsHandler,"/myWs1").addInterceptors(wsInterceptor).setAllowedOriginPatterns("*"); }}
2.4 前端测试
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>wsClient</title></head><body><script> // 创建websocket let ws = new WebSocket("ws://localhost:8080/myWs1") //向服务器发送hello ws.onopen=function (){ ws.send("hello") } //监听数据ws://localhost:8080/myWs ws.onmessage=function (message){ console.log(message.data) }</script></body></html>