基于SpringBoot 2.6.11
1.WebSocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,可以在html页面直接使用。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
过去,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过TCP连接直接交换数据。
当获取WebSocket连接后,可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
WebSocket在传输的过程中不再使用http协议,而是Stomp协议
1.1 STOMP
STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。
STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。
STOMP是一个非常简单和容易实现的协议,其设计灵感源自于HTTP的简单性。尽管STOMP协议在服务器端的实现可能有一定的难度,但客户端的实现却很容易。例如,可以使用Telnet登录到任何的STOMP代理,并与STOMP代理进行交互。
1.2 WebSocket 事件
以下是 WebSocket 对象的相关事件。
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
1.3 WebSocket 方法
以下是 WebSocket 对象的相关方法。
方法 | 描述 |
---|---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
2.用WebSocket实现网页端聊天室
导入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author: Elivs.Xiang* @Email: 406862257@qq.com* @create 2022-12-06 15:09* @verson IDEA 2020.3*/@Configuration
public class WebsocketConfig {@Bean //在容器中创建bean对象,在WebSocketUtil中需要用到的RemoteEndpoint对象public ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
创建工具类
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;import javax.websocket.Session;
import javax.websocket.RemoteEndpoint;
public class WebSocketUtil {//HashMap:不支持多线程,并发情况下线程不安全public static final Map<String, Session> MESSAGEMAP = new ConcurrentHashMap<>();//发送消息给客户端public static void sendMessage(Session session,String message) {if (session!=null) {final RemoteEndpoint.Basic basic = session.getBasicRemote();if (basic!=null) {try {basic.sendText(message);//发送消息回客户端} catch (IOException e) {e.printStackTrace();}}}}//将消息给所有聊天室的人//循环发送public static void sendMessageToAll(String message) {MESSAGEMAP.forEach((sessionId,session)->sendMessage(session, message));}
}
创建handler
import java.io.IOException;
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 com.example.web.util.WebSocketUtil;
import org.springframework.web.bind.annotation.RestController;@RestController
@ServerEndpoint("/WebSocketHandler/{userName}") //表示接受的是STOMP协议提交的数据
public class WebSocketHandler {//建立连接@OnOpenpublic void openSession(@PathParam("userName")String userName,Session session) {//消息String message = "欢迎:"+userName+"加入群聊";//加入聊天室WebSocketUtil.MESSAGEMAP.put(userName, session);//发送消息WebSocketUtil.sendMessageToAll(message);}@OnMessagepublic void onMessage(@PathParam("userName")String userName,String message) {message = userName+":"+message;WebSocketUtil.sendMessageToAll(message);}//离开聊天室@OnClosepublic void onClose(@PathParam("userName")String userName,Session session) {//将当前用户从map中移除 注销WebSocketUtil.MESSAGEMAP.remove(userName);//群发消息WebSocketUtil.sendMessageToAll("用户:"+userName+"离开聊天室");//关闭sessiontry {session.close();} catch (IOException e) {e.printStackTrace();}}//连接异常@OnErrorpublic void onError(Session session,Throwable throwable) {try {session.close();} catch (IOException e) {e.printStackTrace();}}
}
主启动类开启websocket
@SpringBootApplication
@EnableWebSocket
public class WebApplication {public static void main(String[] args) {SpringApplication.run(WebApplication.class, args);}
}
前端代码
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Insert title here</title><link rel="stylesheet" type="text/css" href="css/index.css"><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript">$(document).ready(function(){let url = "ws://192.168.7.131:9988/WebSocketHandler/";let ws = null;//连接服务器$("#join").click(function(){let userName = $("#userName").val();let newUrl = url + userName; //传递用户名console.info(newUrl);//创建对象,连接服务器ws = new WebSocket(newUrl); //html5中提供了//给open事件绑定方法ws.onopen = function(){console.info("连接成功");}//接收到数据ws.onmessage = function(result){var textarea = document.getElementById('textarea');textarea.append(result.data+"\n");//将文本域的滚动条滚动到最后textarea.scrollTop = textarea.scrollHeight;}//关闭连接ws.onclose = function(){$("#textarea").append("用户:"+userName+"离开聊天室"+"\n");console.info("关闭连接");}});//发送消息$("#send").click(function(){//将输入框中的消息发送给服务器,并且显示到消息框中var messageInput = $("#message");var message = messageInput.val();if(ws!=null){ws.send(message); //发送消息messageInput.val("");}});//断开连接$("#out").click(function(){if(ws!=null){ws.close();}});})</script>
</head>
<body>
<div id="box"><p>蜗牛聊天室</p><textarea rows="10" cols="50" disabled="disabled" id="textarea"></textarea><br><div class="infoBox">用户名:<input type="text" id="userName"><br><br><button style="color: green;" id="join">加入聊天室</button> <button style="color: red;" id="out">离开聊天室</button></div><br><br><div class="infoBox">消 息:<input type="text" id="message"><br><br><button id="send">发送消息</button></div><br></div>
</body>
</html>
css
#box{width: 500px;background: pink;text-align: center;
}
.infoBox{text-align:left;position: relative;left: 62px;
}
#message{width: 322px;
}
#send{position:relative;left:50px;height:30px;width: 326px;
}
启动项目报错
Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available
在测试类上的@SpringBootTest中加上
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WebApplicationTests {@Testvoid contextLoads() {}
}
整合Gateway
在application.yml中配置websocket路由
spring:application:name: gatewaycloud:gateway:routes:- id: chat #uri: "ws://127.0.0.1:8000" # ws 协议 需要指定IP,通过微服务名字会报503predicates:- Path=/WebSocketHandler/**