有时候会用到向前端页推送消息的状况,这时候就会用到WebSocket 编程了,javascript
1.在pom.xml 中添加以下配置html
1.1核心是@ServerEndpoint这个注解。这个注解是Javaee标准里的注解,tomcat7以上已经对其进行了实现,若是是用传统方法使用tomcat发布项目,只要在pom文件中引入javaee标准便可使用前端
<dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency>
1.2 但使用springboot的内置tomcat时,就不须要引入javaee-api了,spring-boot已经包含了。使用springboot的websocket功能首先引入springboot组件。java
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>1.3.5.RELEASE</version> </dependency>
1.3. springboot的高级组件会自动引用基础的组件,像spring-boot-starter-websocket就引入了spring-boot-starter-web和spring-boot-starter,因此不要重复引入。web
2. 使用@ServerEndpoint 创立 websocket endpointspring
首先要注入ServerEndointExporTer,这个bean 会自动注册使用了@ServerEndpoint 注解声明的Websocket endpoint.要注意,若是使用独立的servlet容器,而不是直接使用springBoot 内置容器,就不要注入ServerEndpointExporter,由于它将由容器本身提供和管理。编程
配置类的代码以下:api
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
3. 写websocket 的具体实现类,代码以下浏览器
import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; @ServerEndpoint(value = "/websocket/{userCode}") @Component public class MyWebSocket { //静态变量,用来记录当前在线链接数。应该把它设计成线程安全的。 private static int onlineCount = 0; //concurrent包的线程安全Set,用来存放每一个客户端对应的MyWebSocket对象。 private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>(); // 与某个客户的连接会话,须要经过它来给客户端发送数据 private String currentUser; //与某个客户端的链接会话,须要经过它来给客户端发送数据 private Session session; /** * 链接创建成功调用的方法*/ @OnOpen public void onOpen(@PathParam("userCode") String userCode ,Session session) { this.session = session; this.currentUser = userCode; webSocketSet.add(this); //加入set中 addOnlineCount(); //在线数加1 System.out.println("有新链接加入!当前在线人数为" + getOnlineCount()); try { sendMessage("ok"); } catch (IOException e) { System.out.println("IO异常"); } } /** * 链接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); //从set中删除 subOnlineCount(); //在线数减1 System.out.println("有一链接关闭!当前在线人数为" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { System.out.println("来自客户端的消息:" + message); //群发消息 for (MyWebSocket item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * 发生错误时调用 */ @OnError public void onError(Session session, Throwable error) { System.out.println("发生错误"); error.printStackTrace(); } public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message); } public static void sendMessageTo(String message,String userCode) throws IOException{ for(MyWebSocket item :webSocketSet){ if(item.currentUser.equals(userCode)){ item.session.getBasicRemote().sendText(message); } } } /** * 群发自定义消息 * */ public static void sendInfo(String message) throws IOException { for (MyWebSocket item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { MyWebSocket.onlineCount++; } public static synchronized void subOnlineCount() { MyWebSocket.onlineCount--; } }
使用springboot的惟一区别是要@Component声明下,而使用独立容器是由容器本身管理websocket的,但在springboot中连容器都是spring管理的。tomcat
虽然@Component默认是单例模式的,但springboot仍是会为每一个websocket链接初始化一个bean,因此能够用一个静态set保存起来。
4.页面调用简单的方法类
<html> <head> <title> My Websocket</title> </head> <body> Wecome <br/> <input id="text"/><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button> <div id="message"> </div> <script type="text/javascript"> var websocket = null; //判断当前浏览器是否支持webSocket if('WebSocket' in window){ websocket = new WebSocket("ws://127.0.0.1:8988/websocket/1"); }else{ alert("NOt support websocket"); } // 连接发生错误的回调方法 websocket.onerror = function(){ setMessageInnerHTML("error"); } // 连接成功创建的回调方法 websocket.onopen = function(event){ setMessageInnerHTML("open"); } // 接受到消息的回调方法 websocket.onmessage = function(event){ setMessageInnerHTML(event.data); } // 连接关闭的回调方法 websocket.onclose = function(){ setMessageInnerHTML("close"); } //监听窗口关闭事件,当窗口关闭时,主动关闭wesocket 连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function(){ websocket.close(); } // 将消息显示到网页上 function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML+'<br/>'; } // 关闭连接 function closeWebSocket(){ websocket.close(); } //发送消息 function send(){ var message = document.getElementById("text").value; websocket.send(message); } </script> </body> </html>
四、总结
springboot已经作了深度的集成和优化,要注意是否添加了不须要的依赖、配置或声明。因为不少讲解组件使用的文章是和spring集成的,会有一些配置,在使用springboot时,因为springboot已经有了本身的配置,再这些配置有可能致使各类各样的异常。
在webSocket 类中的参数不能引入实体类,这样回报错误,具体为啥报错误,我也没有找到缘由,回头研究清楚了再补充上