基于SpringBoot 2.6.11
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,可以在html页面直接使用。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
过去,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过TCP连接直接交换数据。
当获取WebSocket连接后,可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
WebSocket在传输的过程中不再使用http协议,而是Stomp协议
STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。
STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。
STOMP是一个非常简单和容易实现的协议,其设计灵感源自于HTTP的简单性。尽管STOMP协议在服务器端的实现可能有一定的难度,但客户端的实现却很容易。例如,可以使用Telnet登录到任何的STOMP代理,并与STOMP代理进行交互。
以下是 WebSocket 对象的相关事件。
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 连接建立时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通信发生错误时触发 |
close | Socket.onclose | 连接关闭时触发 |
以下是 WebSocket 对象的相关方法。
方法 | 描述 |
---|---|
Socket.send() | 使用连接发送数据 |
Socket.close() | 关闭连接 |
org.springframework.boot
spring-boot-starter-websocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author: Elivs.Xiang
* @Email: [email protected]
* @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 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));
}
}
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 {
//建立连接
@OnOpen
public void openSession(@PathParam("userName")String userName,Session session) {
//消息
String message = "欢迎:"+userName+"加入群聊";
//加入聊天室
WebSocketUtil.MESSAGEMAP.put(userName, session);
//发送消息
WebSocketUtil.sendMessageToAll(message);
}
@OnMessage
public void onMessage(@PathParam("userName")String userName,String message) {
message = userName+":"+message;
WebSocketUtil.sendMessageToAll(message);
}
//离开聊天室
@OnClose
public void onClose(@PathParam("userName")String userName,Session session) {
//将当前用户从map中移除 注销
WebSocketUtil.MESSAGEMAP.remove(userName);
//群发消息
WebSocketUtil.sendMessageToAll("用户:"+userName+"离开聊天室");
//关闭session
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//连接异常
@OnError
public void onError(Session session,Throwable throwable) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@SpringBootApplication
@EnableWebSocket
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
Insert title here
蜗牛聊天室
用户名:
消 息:
#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 {
@Test
void contextLoads() {
}
}
在application.yml中配置websocket路由
spring:
application:
name: gateway
cloud:
gateway:
routes:
- id: chat #
uri: "ws://127.0.0.1:8000" # ws 协议 需要指定IP,通过微服务名字会报503
predicates:
- Path=/WebSocketHandler/**