WebSocket簡介
WebSocket是一種在單個TCP連接上進行全雙工通信的協議。WebSocket通信協議於2011年被IETF定為標准RFC 6455,並由RFC7936補充規范。WebSocket API也被W3C定為標准。
WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。
Websocket優點
Websocket優點
-
更強的實時性。由於協議是全雙工的,所以服務器可以隨時主動給客戶端下發數據。相對於HTTP請求需要等待客戶端發起請求服務端才能響應,延遲明顯更少;即使是和Comet等類似的長輪詢比較,其也能在短時間內更多次地傳遞數據。
-
保持連接狀態。與HTTP不同的是,Websocket需要先創建連接,這就使得其成為一種有狀態的協議,之后通信時可以省略部分狀態信息。而HTTP請求可能需要在每個請求都攜帶狀態信息(如身份認證等)。
-
更好的二進制支持。Websocket定義了 二進制幀,相對HTTP,可以更輕松地處理二進制內容。
-
可以支持擴展。Websocket定義了擴展,用戶可以擴展協議、實現部分自定義的子協議。如部分瀏覽器支持 壓縮等。
下面實現我們的多人聊天室
簡單看一下我們的效果



這里就是我們一個簡單的效果
再來看一下具體是怎樣實現的
1、首先創建我們的maven項目
2、導入我們的pom依賴
下面是我pom依賴的全部代碼
<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.ybf</groupId> <artifactId>liaotianshi2</artifactId> <version>0.0.1-SNAPSHOT</version> <!-- Spring Boot 啟動父依賴 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.1.RELEASE</version> </parent> <properties> <!-- 項目設置 : 編碼格式 UTF-8 及 springboot 相關版本 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <mybatis-spring-boot>1.2.0</mybatis-spring-boot> <mysql-connector>5.1.39</mysql-connector> <druid>1.0.18</druid> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!-- Spring Boot Test 依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <finalName>liaotianshi</finalName> </build> </project>
3、創建我們的SpringBootWebSocketHandler類
package com.ybf.test; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; @Component public class SpringBootWebSocketHandler implements WebSocketHandler { // 存儲所有客戶端的會話 WebSocketSession,key 使用客戶端的唯一標識方便存取 // private static Map<String, WebSocketSession> allWebSocketSession = new HashMap<String, WebSocketSession>(); private static List<WebSocketSession> sessionList = new ArrayList<>(); /** * 與服務器成功建立連接 */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // TODO Auto-generated method stub System.out.println("客戶端成功建立連接=>" + session.getId()); sessionList.add(session); } /** * 接受客戶端的消息 */ @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { // 獲取客戶端的消息 String msg = message.getPayload().toString(); // System.out.println("接受到的客戶端消息:" + msg); // 將數據存入會話 回傳給客戶端 // session.sendMessage(new TextMessage("服務器接收到的消息:" + msg)); sendMsg(msg); } //給所有客戶端發信息的方法 private void sendMsg(String msg) throws IOException { for (WebSocketSession session : sessionList) { if(session.isOpen()) { session.sendMessage(new TextMessage(msg)); } } } /** * 通訊異常時 */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { // TODO Auto-generated method stub System.out.println("通訊出現異常"); } /** * 當連接關閉時 */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { // TODO Auto-generated method stub System.out.println("連接已關閉"); } /** * 是否允許分段發送 */ @Override public boolean supportsPartialMessages() { // 一次性發送 return false; } }
4、再來創建我們的SpringBootWebSocketConfigurer 類
package com.ybf.test; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration // 單例模式 bean @EnableWebSocket // 啟動 WebSocket 服務器 public class SpringBootWebSocketConfigurer implements WebMvcConfigurer, WebSocketConfigurer { @Autowired private SpringBootWebSocketHandler handler; // @Autowired // private SpringBootHandshakeInterceptor handshakeInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // -------------------- 允許跨域訪問 WebSocket ------------------------ String[] allowsOrigins = { "*" };// 允許連接的域 , 只能以 http 或 https 開頭 // 7. 設置 websocket 服務器地址 ws://localhost:8080/SpringBootWebSocket registry.addHandler(handler, "/SpringBootWebSocket").setAllowedOrigins(allowsOrigins); } /** * 這下面的代碼是沒有什么用的 * 主要是第一個方法 */ @Override public void configurePathMatch(PathMatchConfigurer configurer) { // TODO Auto-generated method stub } @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { // TODO Auto-generated method stub } @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { // TODO Auto-generated method stub } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { // TODO Auto-generated method stub } @Override public void addFormatters(FormatterRegistry registry) { // TODO Auto-generated method stub } @Override public void addInterceptors(InterceptorRegistry registry) { // TODO Auto-generated method stub } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // TODO Auto-generated method stub } @Override public void addCorsMappings(CorsRegistry registry) { // TODO Auto-generated method stub } @Override public void addViewControllers(ViewControllerRegistry registry) { // TODO Auto-generated method stub } @Override public void configureViewResolvers(ViewResolverRegistry registry) { // TODO Auto-generated method stub } @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { // TODO Auto-generated method stub } @Override public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { // TODO Auto-generated method stub } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // TODO Auto-generated method stub } @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // TODO Auto-generated method stub } @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { // TODO Auto-generated method stub } @Override public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { // TODO Auto-generated method stub } @Override public Validator getValidator() { // TODO Auto-generated method stub return null; } @Override public MessageCodesResolver getMessageCodesResolver() { // TODO Auto-generated method stub return null; } }
5、創建我們的前台頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>聊天室</title> <link rel="stylesheet" href="layui/css/layui.css" media="all"> <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script> <script> $(document).ready( function() { var urlPrefix = "ws://localhost:8080/SpringBootWebSocket"; var ws = null; // 加入聊天室 $('#bt_join').click(function() { if(ws != null){ alert("用戶【"+$('#username').val()+"】已加入連接"); return; } //建立連接 //判斷當前瀏覽器是否支持WebSocket if ('WebSocket' in window) { ws = new WebSocket(urlPrefix); } else { alert('當前瀏覽器 Not support websocket'); } ws.onopen = function(event) { console.log("與服務器建立連接"); ws.send("您的好友【"+$('#username').val()+"】上線了"); }; // 接收服務端返回給前端的消息 ws.onmessage = function(event) { $('#text_chat_content').append( event.data + "\n"); }; ws.onclose = function() { console.log("與服務器斷開連接"); $('#text_chat_content').append( "用戶【" + $('#username').val() + "】離開聊天室!" + "\n"); $("#username").val(""); }; }); // 發送消息 $('#bt_send').click(function() { if (ws == null) { alert("該用戶不在線"); return; } var msg = $('#in_msg').val(); ws.send("用戶【" + $('#username').val() + "】:" + msg); }); // 離開聊天室 $('#bt_left').click(function() { ws.send("用戶【" + $('#username').val() + "】離開聊天室!"); $('#in_msg').val(""); ws.close(); }); }) </script> </head> <body> 聊天消息內容: <br /> <textarea id="text_chat_content" readonly="readonly" class="layui-textarea" style="height: 400px; width: 600px;"></textarea> <br /> 輸入框: <br /> <div class="layui-input-inline"> <textarea id="in_msg" placeholder="請輸入內容" class="layui-textarea" style="height: 100px; width: 500px;"> </textarea> </div> <button type="button" id="bt_send" class="layui-btn layui-btn-radius">發送信息</button> <br /> <br /> <label class="layui-form-mid">用戶:</label> <div class="layui-input-inline"> <input type="text" id="username" placeholder="請輸入姓名" class="layui-input"> </div> <button type="button" id="bt_join" class="layui-btn layui-btn-radius layui-btn-normal">加入聊天室</button> <button type="button" id="bt_left" class="layui-btn layui-btn-radius layui-btn-normal">離開聊天室</button> </body> </html>
注意:
我這里是加的 layui 效果,當然也可以不加layui效果,這就看自己的意思了
這是 layui 官網地址 https://www.layui.com/ 大家有興趣可以了解一下
下面就是我的項目框架
