websocket的使用可以使前后端的数据之间进行实时通讯。普通的web程序,由客户端发出请求,由服务端接收请求,进行处理并将结果返回客户端的这么一个流程。可是如果服务端的数据变化频率比较快,客户端要想要比较实时的获得最新的服务端数据,可能需要不断的发送请求,而websoceket进行一次握手之后,浏览器和服务器之间就会建立一条通道,就可以随时进行数据传输。
websocket里面使用@Autowired注入bseries和bean的时候可能会取不到,原因是spring管理的都是单例(singleton),和 websocket (多对象)相冲突。项目启动时初始化,会初始化 websocket (非用户连接的),spring 同时会为其注入 service,该对象的 service 不是 null,被成功注入。但是,由于 spring 默认管理的是单例,所以只会注入一次 service。当新用户进入聊天时,系统又会创建一个新的 websocket 对象,这时矛盾出现了:spring 管理的都是单例,不会给第二个 websocket 对象注入 service,所以导致只要是用户连接创建的 websocket 对象,都不能再注入了。
下面是websocket作为服务端的一个demo:
@ServerEndpoint("/webSocket/{sid}") @Slf4j @Component public class WebSocketServer { /** * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 */ private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); /** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** * 接收sid */ private String sid=""; //存储session的map private static Map<String,Object> dataMap = new HashMap<String,Object>(); /** * 连接建立成功调用的方法 * */ @OnOpen public void onOpen(Session session,@PathParam("sid") String sid) { this.session = session; //将session存起来 dataMap.put(sid,session); //如果存在就先删除一个,防止重复推送消息 for (WebSocketServer webSocket:webSocketSet) { if (webSocket.sid.equals(sid)) { webSocketSet.remove(webSocket); } } webSocketSet.add(this); this.sid=sid; log.info("连接已建立"); } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); log.info("连接已关闭"); } /** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { log.info("收到来"+sid+"的信息:"+message); String sid = getSid(session); if (sid == null) { return; } //群发消息 for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message,sid); } catch (IOException e) { e.printStackTrace(); } } } public String getSid(Session session) { String sid = null; for (String key:dataMap.keySet()) { Session cursession = (Session) dataMap.get(key); if (cursession == session) { sid = key; break; } } return sid; } @OnError public void onError(Session session, Throwable error) { log.error("发生错误"); error.printStackTrace(); } /** * 实现服务器主动推送 */ public void sendMessage(String message, String sid) throws IOException { Session session = (Session) dataMap.get(sid); session.getBasicRemote().sendText(message); } /** * 群发自定义消息 * */ public static void sendInfo(SocketMsg socketMsg, @PathParam("sid") String sid) throws IOException { String message = JSONObject.toJSONString(socketMsg); log.info("推送消息到"+sid+",推送内容:"+message); for (WebSocketServer item : webSocketSet) { try { //这里可以设定只推送给这个sid的,为null则全部推送 if(sid==null) { item.sendMessage(message,sid); }else if(item.sid.equals(sid)){ item.sendMessage(message,sid); } } catch (IOException ignored) { } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } WebSocketServer that = (WebSocketServer) o; return Objects.equals(session, that.session) && Objects.equals(sid, that.sid); } @Override public int hashCode() { return Objects.hash(session, sid); } }
这是websocket作为服务端的其中一种写法,在连接websocket的时候,传入了一个区分用户的ID,并把用户ID存在session中,这里的session代表的是一个websocket连接,这样在服务端向前端返回数据的时候就会知道是给谁发送的信息了。
websocket的传输信息的数据类型好像只有字符串,而且在默认情况下,会有大小的一个限制。解决方式后面补上。
自己测试的时候,可以使用
websocket在线测试
来进行收发信息的测试,注意连接的地址是以 ws:// 开头的,以上面的后端代码为例,@ServerEndpoint("/webSocket/{sid}")注解需要的连接格式为:
ws://127.0.0.1:8080/webSocket/123
连接成功以后就可以收发信息了。