总结:
1.推荐在线测试:在线websocket测试-在线工具-postjson,或写个简单页面(H5或socket.js)
2.两种玩法没多大区别,似乎Javax版本还更easy,但是springboot版本感觉贴近websocket协议本质
3.websocket协议本质—借助HTTP打开TCP长连接通道的全双工的通信协议,避免前端定时器去获取后端数据
4.两者的session都得自己写个并发容器copyOnArraySet /concurrentHashMap等来存放,发送消息时根据条件如uid去并发容器中找到该session进行后端消息往前端实时推送。
普通玩法
1.依赖
普通玩法可以只导这个包,比较小,也可以导springboot的spring-boot-start-websocket,间接引入的前者
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
2.javaX版本处理器
@ServerEndpoint("/ws/javax/{uid}")
@Component
public class JavaXWebSocket {
@OnOpen
public void onOpen(Session session, @PathParam(value = "uid")String uid) throws IOException {
System.out.println("websocket已经连接" + session);
session.getBasicRemote().sendText(uid+",你好!欢迎登陆系统");
}
@OnClose
public void onClose(Session session){
System.out.println("websocket已经关闭" + session);
}
@OnMessage
public void onMessage(String message, Session session) throws IOException {
System.out.println("收到客户端发来的消息 --> " + message);
session.getBasicRemote().sendText("消息已收到");
}
}
3.还需要配置一个bean-端点导出器,在启动类或专门写个配置类丢进去就好了
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
springboot玩法
1.依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2..JavaX是path上获取参数,springboot是通过拦截器request对象获取到参数,再将这个参数存入websocketSession对象
@Component
public class MyHandshakeInterceptor implements HandshakeInterceptor {
/**
* 握手之前,若返回false,则不建立链接
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse
response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
//将用户id放入socket处理器的会话(WebSocketSession)中
attributes.put("uid", ((ServletServerHttpRequest) request).getServletRequest().getParameter("uid"));
System.out.println("开始握手。。。。。。。");
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse
response, WebSocketHandler wsHandler, Exception exception) {
System.out.println("握手成功啦。。。。。。");
}
}
3.springboot版本处理器,需继承TextWebSocketHandler,参数从WebSocketSession获取
@Component
public class MyHandler extends TextWebSocketHandler {
private static final Map<Long,WebSocketSession> sessions = new HashMap<>();
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
throws IOException {
System.out.println("获取到消息 >> " + message.getPayload());
if(toSession != null && toSession.isOpen()){ //有session且open状态
session.sendMessage(new TextMessage("消息已收到"));
}
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws
Exception {
String uid = (String)session.getAttributes().get("uid");
session.sendMessage(new TextMessage(uid+", 你好!欢迎连接到ws服务"));
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
throws Exception {
System.out.println("断开连接!");
}
}
4.配置类
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Autowired
private MyHandler myHandler;
@Autowired
private MyHandshakeInterceptor myHandshakeInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(this.myHandler, "/ws/springboot") //相等于@ServerEndPoint
.setAllowedOrigins("*") //跨域支持
.addInterceptors(this.myHandshakeInterceptor) //添加拦截器
.withSockJS(); //支持socket.js
}
}
# 优化
1.前端STOMP协议才需要开启SOCKJS
2.Handler实现不需要放入spring容器
3.只是粗糙的广播,使用SET来存储session即可,(session有绑定id...),如果需要发给指定User的场景可以使用Map<Long,WebSocketSession>存储,K为userId.
```java
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new MyHandler2(), "/ws/springboot2") //相等于@ServerEndPoint .setAllowedOrigins("*"); //跨域支持 //.withSockJS(); //支持socket.js 前端 } }
```
广播案例
```java
package com.example.demo.websocket; import lombok.SneakyThrows; import org.springframework.web.socket.*; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; import java.util.*; public class MyHandler2 extends TextWebSocketHandler { private static final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>()); @SneakyThrows public static void sendAll(WebSocketSession selfSession){ sessions.stream().parallel().forEach(webSocketSession -> { if (!webSocketSession.getId().equals(selfSession.getId())) { try { webSocketSession.sendMessage(new TextMessage("XXXX")); } catch (IOException e) { e.printStackTrace(); } } }); } @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { //super.handleTextMessage(session,message); sendAll(session); } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); super.afterConnectionEstablished(session); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { super.handleMessage(session, message); } @Override protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { super.handlePongMessage(session, message); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { exception.printStackTrace(); super.handleTransportError(session, exception); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); super.afterConnectionClosed(session, status); } }
```