Java中Spring Boot+Socket實現(xiàn)與html頁面的長連接實例詳解
Spring Boot+Socket實現(xiàn)與html頁面的長連接,客戶端給服務(wù)器端發(fā)消息,服務(wù)器給客戶端輪詢發(fā)送消息,附案例源碼
功能介紹
客戶端給所有在線用戶發(fā)送消息客戶端給指定在線用戶發(fā)送消息服務(wù)器給客戶端發(fā)送消息(輪詢方式)
注意:socket只是實現(xiàn)一些簡單的功能,具體的還需根據(jù)自身情況,代碼稍微改造下
項目搭建
項目結(jié)構(gòu)圖

pom.xml
<?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.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cyb</groupId>
<artifactId>socket_test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>socket_test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- springboot websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--guava依賴-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<!--fastjson依賴-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
appliccation.properties

SocketTestApplication.java(Spring Boot啟動類)

WebSocketStompConfig.java

package com.cyb.socket.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketStompConfig {
//這個bean的注冊,用于掃描帶有@ServerEndpoint的注解成為websocket ,如果你使用外置的tomcat就不需要該配置文件
@Bean
public ServerEndpointExporter serverEndpointExporter()
{
return new ServerEndpointExporter();
}
}
WebSocket.java(Socket核心類)

package com.cyb.socket.websocket;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @Author:陳彥斌
* @Description:Socket核心類
* @Date: 2020-07-26
*/
@Component
@ServerEndpoint(value = "/connectWebSocket/{userId}")
public class WebSocket {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 在線人數(shù)
*/
public static int onlineNumber = 0;
/**
* 以用戶的姓名為key,WebSocket為對象保存起來
*/
private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
/**
* 會話
*/
private Session session;
/**
* 用戶名稱
*/
private String userId;
/**
* 建立連接
*
* @param session
*/
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) {
onlineNumber++;
System.out.println("現(xiàn)在來連接的客戶id:" + session.getId() + "用戶名:" + userId);
//logger.info("現(xiàn)在來連接的客戶id:"+session.getId()+"用戶名:"+userId);
this.userId = userId;
this.session = session;
System.out.println("有新連接加入! 當(dāng)前在線人數(shù)" + onlineNumber);
// logger.info("有新連接加入! 當(dāng)前在線人數(shù)" + onlineNumber);
try {
//messageType 1代表上線 2代表下線 3代表在線名單 4代表普通消息
//先給所有人發(fā)送通知,說我上線了
Map<String, Object> map1 = Maps.newHashMap();
map1.put("messageType", 1);
map1.put("userId", userId);
sendMessageAll(JSON.toJSONString(map1), userId);
//把自己的信息加入到map當(dāng)中去
clients.put(userId, this);
System.out.println("有連接關(guān)閉! 當(dāng)前在線人數(shù)" + onlineNumber);
//logger.info("有連接關(guān)閉! 當(dāng)前在線人數(shù)" + clients.size());
//給自己發(fā)一條消息:告訴自己現(xiàn)在都有誰在線
Map<String, Object> map2 = Maps.newHashMap();
map2.put("messageType", 3);
//移除掉自己
Set<String> set = clients.keySet();
map2.put("onlineUsers", set);
sendMessageTo(JSON.toJSONString(map2), userId);
} catch (IOException e) {
System.out.println(userId + "上線的時候通知所有人發(fā)生了錯誤");
//logger.info(userId+"上線的時候通知所有人發(fā)生了錯誤");
}
}
@OnError
public void onError(Session session, Throwable error) {
//logger.info("服務(wù)端發(fā)生了錯誤"+error.getMessage());
//error.printStackTrace();
System.out.println("服務(wù)端發(fā)生了錯誤:" + error.getMessage());
}
/**
* 連接關(guān)閉
*/
@OnClose
public void onClose() {
onlineNumber--;
//webSockets.remove(this);
clients.remove(userId);
try {
//messageType 1代表上線 2代表下線 3代表在線名單 4代表普通消息
Map<String, Object> map1 = Maps.newHashMap();
map1.put("messageType", 2);
map1.put("onlineUsers", clients.keySet());
map1.put("userId", userId);
sendMessageAll(JSON.toJSONString(map1), userId);
} catch (IOException e) {
System.out.println(userId + "下線的時候通知所有人發(fā)生了錯誤");
//logger.info(userId+"下線的時候通知所有人發(fā)生了錯誤");
}
//logger.info("有連接關(guān)閉! 當(dāng)前在線人數(shù)" + onlineNumber);
//logger.info("有連接關(guān)閉! 當(dāng)前在線人數(shù)" + clients.size());
System.out.println("有連接關(guān)閉! 當(dāng)前在線人數(shù)" + onlineNumber);
}
/**
* 收到客戶端的消息
*
* @param message 消息
* @param session 會話
*/
@OnMessage
public void onMessage(String message, Session session) {
try {
//logger.info("來自客戶端消息:" + message+"客戶端的id是:"+session.getId());
System.out.println("來自客戶端消息:" + message + " | 客戶端的id是:" + session.getId());
JSONObject jsonObject = JSON.parseObject(message);
String textMessage = jsonObject.getString("message");
String fromuserId = jsonObject.getString("userId");
String touserId = jsonObject.getString("to");
//如果不是發(fā)給所有,那么就發(fā)給某一個人
//messageType 1代表上線 2代表下線 3代表在線名單 4代表普通消息
Map<String, Object> map1 = Maps.newHashMap();
map1.put("messageType", 4);
map1.put("textMessage", textMessage);
map1.put("fromuserId", fromuserId);
if (touserId.equals("All")) {
map1.put("touserId", "所有人");
sendMessageAll(JSON.toJSONString(map1), fromuserId);
} else {
map1.put("touserId", touserId);
System.out.println("開始推送消息給" + touserId);
sendMessageTo(JSON.toJSONString(map1), touserId);
}
} catch (Exception e) {
e.printStackTrace();
//logger.info("發(fā)生了錯誤了");
}
}
/**
* 給指定的用戶發(fā)送消息
*
* @param message
* @param TouserId
* @throws IOException
*/
public void sendMessageTo(String message, String TouserId) throws IOException {
for (WebSocket item : clients.values()) {
System.out.println("給指定的在線用戶發(fā)送消息,在線人員名單:【" + item.userId.toString() + "】發(fā)送消息:" + message);
if (item.userId.equals(TouserId)) {
item.session.getAsyncRemote().sendText(message);
break;
}
}
}
/**
* 給所有用戶發(fā)送消息
*
* @param message 數(shù)據(jù)
* @param FromuserId
* @throws IOException
*/
public void sendMessageAll(String message, String FromuserId) throws IOException {
for (WebSocket item : clients.values()) {
System.out.println("給所有在線用戶發(fā)送給消息,在線人員名單:【" + item.userId.toString() + "】發(fā)送消息:" + message);
item.session.getAsyncRemote().sendText(message);
}
}
/**
* 給所有在線用戶發(fā)送消息
*
* @param message 數(shù)據(jù)
* @throws IOException
*/
public void sendMessageAll(String message) throws IOException {
for (WebSocket item : clients.values()) {
System.out.println("服務(wù)器給所有在線用戶發(fā)送消息,當(dāng)前在線人員為【" + item.userId.toString() + "】發(fā)送消息:" + message);
item.session.getAsyncRemote().sendText(message);
}
}
/**
* 獲取在線用戶數(shù)
*
* @return
*/
public static synchronized int getOnlineCount() {
return onlineNumber;
}
}
TestController.java(前端控制器)

package com.cyb.socket.websocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
@Controller
@RequestMapping("testMethod")
public class TestController {
@Autowired
private WebSocket webSocket;
/**
* 給指定的在線用戶發(fā)送消息
* @param userId
* @param msg
* @return
* @throws IOException
*/
@ResponseBody
@GetMapping("/sendTo")
public String sendTo(@RequestParam("userId") String userId,@RequestParam("msg") String msg) throws IOException {
webSocket.sendMessageTo(msg,userId);
return "推送成功";
}
/**
* 給所有在線用戶發(fā)送消息
* @param msg
* @return
* @throws IOException
* @throws IOException
*/
@ResponseBody
@PostMapping("/sendAll")
public String sendAll(@RequestBody String msg) throws IOException, IOException {
webSocket.sendMessageAll(msg);
return "推送成功";
}
}
SocketTask.java(輪詢調(diào)度往客戶端推送消息)

package com.cyb.socket.schedule;
import com.cyb.socket.websocket.WebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Component
public class SocketTask {
@Autowired
private WebSocket webSocket;
private SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
//5秒輪詢一次
@Scheduled(fixedRate = 5000)
public void sendClientData() throws IOException {
String msg="{\"message\":\"你好\",\"userId\":\"002\",\"to\":\"All\"}";
webSocket.sendMessageAll(msg);
System.out.println("消息推送時間:"+ sdf.format(new Date()));
}
}
測試網(wǎng)頁
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>Test My WebSocket</title>
</head>
<body>
TestWebSocket
<input id="text" type="text" style="width:500px"/>
<button onclick="send()">SEND MESSAGE</button>
<button onclick="closeWebSocket()">CLOSE</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判斷當(dāng)前瀏覽器是否支持WebSocket
if('WebSocket' in window){
//連接WebSocket節(jié)點
websocket = new WebSocket("ws://localhost:8083/connectWebSocket/001");
}
else{
alert('Not support websocket')
}
//連接發(fā)生錯誤的回調(diào)方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//連接成功建立的回調(diào)方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
//接收到消息的回調(diào)方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//連接關(guān)閉的回調(diào)方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。
window.onbeforeunload = function(){
websocket.close();
}
//將消息顯示在網(wǎng)頁上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//關(guān)閉連接
function closeWebSocket(){
websocket.close();
}
//發(fā)送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
index2.html
<!DOCTYPE HTML>
<html>
<head>
<title>Test My WebSocket</title>
</head>
<body>
TestWebSocket
<input id="text" type="text" style="width:500px" />
<button onclick="send()">SEND MESSAGE</button>
<button onclick="closeWebSocket()">CLOSE</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
//判斷當(dāng)前瀏覽器是否支持WebSocket
if('WebSocket' in window){
//連接WebSocket節(jié)點
websocket = new WebSocket("ws://localhost:8083/connectWebSocket/002");
}
else{
alert('Not support websocket')
}
//連接發(fā)生錯誤的回調(diào)方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//連接成功建立的回調(diào)方法
websocket.onopen = function(event){
setMessageInnerHTML("open");
}
//接收到消息的回調(diào)方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//連接關(guān)閉的回調(diào)方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。
window.onbeforeunload = function(){
websocket.close();
}
//將消息顯示在網(wǎng)頁上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//關(guān)閉連接
function closeWebSocket(){
websocket.close();
}
//發(fā)送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
</script>
</html>
項目地址
鏈接:https://pan.baidu.com/s/1yiAXTkCjHac-F3S1HFyNJQ 提取碼:53tp
功能演示
客戶端給所有在線用戶發(fā)消息

客戶端給指定在線用戶發(fā)送消息

服務(wù)器給客戶端發(fā)送消息(輪詢方式)
注意需要加上這些注解

演示

通過前端控制器給指定用戶發(fā)送消息

演示

到此這篇關(guān)于Java中Spring Boot+Socket實現(xiàn)與html頁面的長連接實例詳解的文章就介紹到這了,更多相關(guān)Java Spring Boot+Socket實現(xiàn)與html頁面的長連接內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java String 和 new String()的比較與區(qū)別
這篇文章主要介紹了Java String 和 new String()的區(qū)別的相關(guān)資料,需要的朋友可以參考下2017-04-04
Mybatis中foreach標(biāo)簽帶來的空格\換行\(zhòng)回車問題及解決方案
這篇文章主要介紹了解決Mybatis中foreach標(biāo)簽帶來的空格,換行,回車問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
java快速解析路徑中的參數(shù)(&與=拼接的參數(shù))
這篇文章主要介紹了java快速解析路徑中的參數(shù)(&與=拼接的參數(shù)),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-02-02
java使用靜態(tài)關(guān)鍵字實現(xiàn)單例模式
這篇文章主要為大家詳細(xì)介紹了java使用靜態(tài)關(guān)鍵字實現(xiàn)單例模式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04
maven繼承父工程統(tǒng)一版本號的實現(xiàn)
這篇文章主要介紹了maven繼承父工程統(tǒng)一版本號的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Spring?Boot?集成Elasticsearch模塊實現(xiàn)簡單查詢功能
本文講解了Spring?Boot集成Elasticsearch采用的是ES模板的方式實現(xiàn)基礎(chǔ)查詢,本文結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2022-06-06

