一、websocket的介紹
WebSocket是一種網(wǎng)絡(luò)通信協(xié)議,它可以在單個TCP連接上實現(xiàn)雙向(全雙工)通信。WebSocket使用HTML5標準,并且可以在客戶端和服務(wù)器之間建立持久連接,這意味著連接在瀏覽器刷新或關(guān)閉后仍然保持打開狀態(tài)。
WebSocket的主要優(yōu)點包括:
1. 雙向通信:WebSocket支持客戶端和服務(wù)器之間的雙向通信,這使得實時數(shù)據(jù)傳輸變得非常容易。
2. 持久連接:WebSocket使用單個TCP連接,這使得服務(wù)器可以保持與客戶端的通信,即使瀏覽器被關(guān)閉或刷新。
3. 零延遲:由于WebSocket使用單個TCP連接,因此數(shù)據(jù)傳輸不會出現(xiàn)延遲。
4. 更好的性能:WebSocket使用二進制傳輸,這使得數(shù)據(jù)傳輸速度更快。
5. 更好的可伸縮性:WebSocket可以處理大量的并發(fā)連接,因為它使用單個TCP連接。
二、springboot集成websocket(用注解的方式)
1.創(chuàng)建一個springboot項目,測試是否成功
package com.dong.websocket.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping(value = "/")
public String index() {
return "Hello World!";
}
}
2.目錄
3.依賴
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dong</groupId>
<artifactId>springboot_websocket</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>SpringBootProject</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4.配置類
package com.dong.websocket.websocket;
import javax.websocket.server.ServerEndpoint;
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();
}
}
5.監(jiān)聽類
package com.dong.websocket.websocket;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
/**
* 監(jiān)聽websocket地址/myWs
*/
@ServerEndpoint("/myWs")
@Component
@Slf4j
public class WsServeEndPont {
// 線程安全的Map,用來保存session, static用來確保是屬于類的而不是對象的
static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
// 連接建立時執(zhí)行的操作
@OnOpen
public void OnOpen (Session session) {
sessionMap.put(session.getId(), session);
log.info("websocket is open");
}
// 收到客戶端消息執(zhí)行的操作
@OnMessage
public String onMessage(String text) {
log.info("收到了一條消息:" + text);
return "已收到你的消息";
}
// 連接關(guān)閉時執(zhí)行的操作
@OnClose
public void OnClose (Session session) {
sessionMap.remove(session.getId());
log.info("websocket is close");
}
// 定時任務(wù):間隔時間2s
@Scheduled(fixedRate = 2000)
public void sendMsg () {
sessionMap.keySet().forEach(item->{
try {
sessionMap.get(item).getBasicRemote().sendText("心跳");
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
6.前端頁面ws-client
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ws client</title>
</head>
<body>
</body>
<script>
let ws = new WebSocket("ws://localhost:8080/myWs")
ws.onopen= function () {
ws.send("hello")
}
ws.onmessage = function (message) {
console.log(message.data)
}
</script>
</html>
三、springboot集成websocket(用spring框架實現(xiàn))
1.Spring提供的類和接口
- HttpSessionHandshakeInterceptor(抽象類):握手攔截器,在握手錢后添加操作
- AbstractWebSocketHandler(抽象類):WebSocket處理程序,監(jiān)聽連接前,連接中,連接后
- WebSocketConfigurer(接口):配置程序,比如配置監(jiān)聽哪個端口,自定義的握手攔截器,處理程序的使用
2.項目結(jié)構(gòu)
?3.SessionBean類,封裝websocketsession和用戶id標識
package com.dong.websocket.spring;
import org.springframework.web.socket.WebSocketSession;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class SessionBean {
private WebSocketSession webSocketSession;
private Integer clientId;
}
4.MyWsInterceptor類,握手攔截器
package com.dong.websocket.spring;
import java.util.Map;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import lombok.extern.slf4j.Slf4j;
/**
* 握手攔截器
*/
@Component
@Slf4j
public class MyWsInterceptor extends HttpSessionHandshakeInterceptor{
@Override
public boolean beforeHandshake (ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes ) throws Exception {
log.info(request.getRemoteAddress().toString() + "開始握手");
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
log.info(request.getRemoteAddress().toString() + "完成握手");
super.afterHandshake(request, response, wsHandler, ex);
}
}
5.MyWsHander類
package com.dong.websocket.spring;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import lombok.extern.slf4j.Slf4j;
/**
* web socket 主處理程序
*/
@Slf4j
@Component
public class MyWsHandler extends AbstractWebSocketHandler {
private static Map<String,SessionBean> sessionBeanMap ;
private static AtomicInteger clientIdMaker;
private static StringBuffer stringBuffer;
static {
sessionBeanMap = new ConcurrentHashMap<>();
clientIdMaker = new AtomicInteger(0);
stringBuffer = new StringBuffer();
}
//連接建立
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
SessionBean sessionBean = new SessionBean(session,clientIdMaker.getAndIncrement());
sessionBeanMap.put(session.getId(),sessionBean);
log.info(sessionBeanMap.get(session.getId()).getClientId()+"建立了連接");
stringBuffer.append(sessionBeanMap.get(session.getId()).getClientId()+"進入了群聊<br/>");
sendMessage(sessionBeanMap);
}
//收到消息
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
log.info(sessionBeanMap.get(session.getId()).getClientId()+":"+message.getPayload());
stringBuffer.append(sessionBeanMap.get(session.getId()).getClientId()+":"+message.getPayload()+"<br/>");
sendMessage(sessionBeanMap);
}
//傳輸異常
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
super.handleTransportError(session, exception);
if(session.isOpen()){
session.close();
}
sessionBeanMap.remove(session.getId());
}
//連接關(guān)閉
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
int clientId = sessionBeanMap.get(session.getId()).getClientId();
sessionBeanMap.remove(session.getId());
log.info(clientId+"關(guān)閉了連接");
stringBuffer.append(clientId+"退出了群聊<br/>");
sendMessage(sessionBeanMap);
}
// //每2s發(fā)送給客戶端心跳消息
// @Scheduled(fixedRate = 2000)
// public void sendMsg() throws IOException {
// for(String key:sessionBeanMap.keySet()){
// sessionBeanMap.get(key).getWebSocketSession().sendMessage(new TextMessage("心跳"));
// }
// }
public void sendMessage(Map<String,SessionBean> sessionBeanMap){
for(String key:sessionBeanMap.keySet()){
try {
sessionBeanMap.get(key).getWebSocketSession().sendMessage(new TextMessage(stringBuffer.toString()));
} catch (IOException e) {
// e.printStackTrace();
log.error(e.getMessage());
}
}
}
}
6.MyWsConfig類
package com.dong.websocket.spring;
import javax.annotation.Resource;
import org.springframework.context.annotation.Configuration;
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
@EnableWebSocket
public class MyWsConfig implements WebSocketConfigurer {
@Resource
MyWsHandler myWsHandler;
@Resource
MyWsInterceptor myWsInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myWsHandler, "/myWs1").addInterceptors(myWsInterceptor).setAllowedOrigins("*");
}
}
四、實現(xiàn)一個簡單的聊天室功能
1.ws-client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ws client</title>
</head>
<body>
<p style="border:1px solid black;width: 600px;height: 500px" id="talkMsg"></p>
<input id="message" /><button id="sendBtn" onclick="sendMsg()">發(fā)送</button>
</body>
<script>
let ws = new WebSocket("ws://localhost:8080/myWs1")
// ws.onopen=function () {
// }
ws.onmessage=function (message) {
document.getElementById("talkMsg").innerHTML = message.data
}
function sendMsg() {
ws.send(document.getElementById("message").value)
document.getElementById("message").value=""
}
</script>
</html>
2.MyWsHandler
package com.dong.websocket.spring;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import lombok.extern.slf4j.Slf4j;
/**
* web socket 主處理程序
*/
@Slf4j
@Component
public class MyWsHandler extends AbstractWebSocketHandler {
private static Map<String,SessionBean> sessionBeanMap ;
private static AtomicInteger clientIdMaker;
private static StringBuffer stringBuffer;
static {
sessionBeanMap = new ConcurrentHashMap<>();
clientIdMaker = new AtomicInteger(0);
stringBuffer = new StringBuffer();
}
//連接建立
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
SessionBean sessionBean = new SessionBean(session,clientIdMaker.getAndIncrement());
sessionBeanMap.put(session.getId(),sessionBean);
log.info(sessionBeanMap.get(session.getId()).getClientId()+"建立了連接");
stringBuffer.append(sessionBeanMap.get(session.getId()).getClientId()+"進入了群聊<br/>");
sendMessage(sessionBeanMap);
}
//收到消息
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
log.info(sessionBeanMap.get(session.getId()).getClientId()+":"+message.getPayload());
stringBuffer.append(sessionBeanMap.get(session.getId()).getClientId()+":"+message.getPayload()+"<br/>");
sendMessage(sessionBeanMap);
}
//傳輸異常
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
super.handleTransportError(session, exception);
if(session.isOpen()){
session.close();
}
sessionBeanMap.remove(session.getId());
}
//連接關(guān)閉
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
int clientId = sessionBeanMap.get(session.getId()).getClientId();
sessionBeanMap.remove(session);
log.info(clientId+"關(guān)閉了連接");
stringBuffer.append(clientId+"退出了群聊<br/>");
sendMessage(sessionBeanMap);
}
// //每2s發(fā)送給客戶端心跳消息
// @Scheduled(fixedRate = 2000)
// public void sendMsg() throws IOException {
// for(String key:sessionBeanMap.keySet()){
// sessionBeanMap.get(key).getWebSocketSession().sendMessage(new TextMessage("心跳"));
// }
// }
public void sendMessage(Map<String,SessionBean> sessionBeanMap){
for(String key:sessionBeanMap.keySet()){
try {
sessionBeanMap.get(key).getWebSocketSession().sendMessage(new TextMessage(stringBuffer.toString()));
} catch (IOException e) {
// e.printStackTrace();
log.error(e.getMessage());
}
}
}
}
效果:
第一個頁面打開:
?第二個頁面打開并聊天
文章來源:http://www.zghlxwxcb.cn/news/detail-842196.html
demo鏈接:springboot-demo集合: demo集合 - Gitee.com文章來源地址http://www.zghlxwxcb.cn/news/detail-842196.html
到了這里,關(guān)于Springboot + Websocket的集成實現(xiàn)簡單的聊天室功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!