SpringBoot+netty-socketio實(shí)現(xiàn)服務(wù)器端消息推送
首先:因?yàn)楣ぷ餍枰枰獙?duì)接socket.io框架對(duì)接,所以目前只能使用netty-socketio。websocket是不支持對(duì)接socket.io框架的。
netty-socketio顧名思義他是一個(gè)底層基于netty'實(shí)現(xiàn)的socket。
在springboot項(xiàng)目中的集成,請(qǐng)看下面的代碼
maven依賴
<dependency> <groupId>com.corundumstudio.socketio</groupId> <artifactId>netty-socketio</artifactId> <version>1.7.11</version> </dependency>
下面就是代碼了
首先是配置參數(shù)
#socketio配置 socketio: host: localhost port: 9099 # 設(shè)置最大每幀處理數(shù)據(jù)的長(zhǎng)度,防止他人利用大數(shù)據(jù)來(lái)攻擊服務(wù)器 maxFramePayloadLength: 1048576 # 設(shè)置http交互最大內(nèi)容長(zhǎng)度 maxHttpContentLength: 1048576 # socket連接數(shù)大?。ㄈ缰槐O(jiān)聽(tīng)一個(gè)端口boss線程組為1即可) bossCount: 1 workCount: 100 allowCustomRequests: true # 協(xié)議升級(jí)超時(shí)時(shí)間(毫秒),默認(rèn)10秒。HTTP握手升級(jí)為ws協(xié)議超時(shí)時(shí)間 upgradeTimeout: 1000000 # Ping消息超時(shí)時(shí)間(毫秒),默認(rèn)60秒,這個(gè)時(shí)間間隔內(nèi)沒(méi)有接收到心跳消息就會(huì)發(fā)送超時(shí)事件 pingTimeout: 6000000 # Ping消息間隔(毫秒),默認(rèn)25秒??蛻舳讼蚍?wù)器發(fā)送一條心跳消息間隔 pingInterval: 25000
上面的注釋寫的很清楚。下面是config代碼
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketConfig;
import com.corundumstudio.socketio.SocketIOServer;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* kcm
*/
@Component
public class PushServer implements InitializingBean {
@Autowired
private EventListenner eventListenner;
@Value("${socketio.port}")
private int serverPort;
@Value("${socketio.host}")
private String serverHost;
@Value("${socketio.bossCount}")
private int bossCount;
@Value("${socketio.workCount}")
private int workCount;
@Value("${socketio.allowCustomRequests}")
private boolean allowCustomRequests;
@Value("${socketio.upgradeTimeout}")
private int upgradeTimeout;
@Value("${socketio.pingTimeout}")
private int pingTimeout;
@Value("${socketio.pingInterval}")
private int pingInterval;
@Override
public void afterPropertiesSet() throws Exception {
Configuration config = new Configuration();
config.setPort(serverPort);
config.setHostname(serverHost);
config.setBossThreads(bossCount);
config.setWorkerThreads(workCount);
config.setAllowCustomRequests(allowCustomRequests);
config.setUpgradeTimeout(upgradeTimeout);
config.setPingTimeout(pingTimeout);
config.setPingInterval(pingInterval);
SocketConfig socketConfig = new SocketConfig();
socketConfig.setReuseAddress(true);
socketConfig.setTcpNoDelay(true);
socketConfig.setSoLinger(0);
config.setSocketConfig(socketConfig);
SocketIOServer server = new SocketIOServer(config);
server.addListeners(eventListenner);
server.start();
System.out.println("啟動(dòng)正常");
}
}
在就是監(jiān)聽(tīng)代碼
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import org.apache.commons.lang3.StringUtils;
import org.bangying.auth.JwtSupport;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.UUID;
@Component
public class EventListenner {
@Resource
private ClientCache clientCache;
@Resource
private JwtSupport jwtSupport;
/**
* 客戶端連接
*
* @param client
*/
@OnConnect
public void onConnect(SocketIOClient client) {
String userId = client.getHandshakeData().getSingleUrlParam("userId");
// userId = jwtSupport.getApplicationUser().getId().toString();
// userId = "8";
UUID sessionId = client.getSessionId();
clientCache.saveClient(userId, sessionId, client);
System.out.println("建立連接");
}
/**
* 客戶端斷開(kāi)
*
* @param client
*/
@OnDisconnect
public void onDisconnect(SocketIOClient client) {
String userId = client.getHandshakeData().getSingleUrlParam("userId");
if (StringUtils.isNotBlank(userId)) {
clientCache.deleteSessionClient(userId, client.getSessionId());
System.out.println("關(guān)閉連接");
}
}
//消息接收入口,當(dāng)接收到消息后,查找發(fā)送目標(biāo)客戶端,并且向該客戶端發(fā)送消息,且給自己發(fā)送消息
// 暫未使用
@OnEvent("messageevent")
public void onEvent(SocketIOClient client, AckRequest request) {
}
}
本地緩存信息
import com.corundumstudio.socketio.SocketIOClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* kcm
*/
@Component
public class ClientCache {
//本地緩存
private static Map<String, HashMap<UUID, SocketIOClient>> concurrentHashMap=new ConcurrentHashMap<>();
/**
* 存入本地緩存
* @param userId 用戶ID
* @param sessionId 頁(yè)面sessionID
* @param socketIOClient 頁(yè)面對(duì)應(yīng)的通道連接信息
*/
public void saveClient(String userId, UUID sessionId,SocketIOClient socketIOClient){
if(StringUtils.isNotBlank(userId)){
HashMap<UUID, SocketIOClient> sessionIdClientCache=concurrentHashMap.get(userId);
if(sessionIdClientCache==null){
sessionIdClientCache = new HashMap<>();
}
sessionIdClientCache.put(sessionId,socketIOClient);
concurrentHashMap.put(userId,sessionIdClientCache);
}
}
/**
* 根據(jù)用戶ID獲取所有通道信息
* @param userId
* @return
*/
public HashMap<UUID, SocketIOClient> getUserClient(String userId){
return concurrentHashMap.get(userId);
}
/**
* 根據(jù)用戶ID及頁(yè)面sessionID刪除頁(yè)面鏈接信息
* @param userId
* @param sessionId
*/
public void deleteSessionClient(String userId,UUID sessionId){
concurrentHashMap.get(userId).remove(sessionId);
}
}
下面是存儲(chǔ)客戶端連接信息
import com.corundumstudio.socketio.SocketIOClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* kcm
*/
@Component
public class ClientCache {
//本地緩存
private static Map<String, HashMap<UUID, SocketIOClient>> concurrentHashMap=new ConcurrentHashMap<>();
/**
* 存入本地緩存
* @param userId 用戶ID
* @param sessionId 頁(yè)面sessionID
* @param socketIOClient 頁(yè)面對(duì)應(yīng)的通道連接信息
*/
public void saveClient(String userId, UUID sessionId,SocketIOClient socketIOClient){
if(StringUtils.isNotBlank(userId)){
HashMap<UUID, SocketIOClient> sessionIdClientCache=concurrentHashMap.get(userId);
if(sessionIdClientCache==null){
sessionIdClientCache = new HashMap<>();
}
sessionIdClientCache.put(sessionId,socketIOClient);
concurrentHashMap.put(userId,sessionIdClientCache);
}
}
/**
* 根據(jù)用戶ID獲取所有通道信息
* @param userId
* @return
*/
public HashMap<UUID, SocketIOClient> getUserClient(String userId){
return concurrentHashMap.get(userId);
}
/**
* 根據(jù)用戶ID及頁(yè)面sessionID刪除頁(yè)面鏈接信息
* @param userId
* @param sessionId
*/
public void deleteSessionClient(String userId,UUID sessionId){
concurrentHashMap.get(userId).remove(sessionId);
}
}
控制層推送方法
@RestController
@RequestMapping("/push")
public class PushController {
@Resource
private ClientCache clientCache;
@Autowired
private JwtSupport jwtSupport;
@GetMapping("/message")
public String pushTuUser(@Param("id") String id){
Integer userId = jwtSupport.getApplicationUser().getId();
HashMap<UUID, SocketIOClient> userClient = clientCache.getUserClient(String.valueOf(userId));
userClient.forEach((uuid, socketIOClient) -> {
//向客戶端推送消息
socketIOClient.sendEvent("chatevent","服務(wù)端推送消息");
});
return "success";
}
}
到此這篇關(guān)于SpringBoot+netty-socketio實(shí)現(xiàn)服務(wù)器端消息推送的文章就介紹到這了,更多相關(guān)SpringBoot netty-socketio服務(wù)器端推送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
本文給大家分享的是一則使用java編寫的文件管理器的代碼,新人練手的作品,邏輯上還是有點(diǎn)小問(wèn)題,大家?guī)兔纯窗伞?/div> 2015-04-04
springboot實(shí)現(xiàn)熱部署操作方法
這篇文章主要介紹了springboot實(shí)現(xiàn)熱部署操作方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11
配置springboot項(xiàng)目使用外部tomcat過(guò)程解析
這篇文章主要介紹了配置springboot項(xiàng)目使用外部tomcat過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Spring Security 實(shí)現(xiàn)用戶名密碼登錄流程源碼詳解
在服務(wù)端的安全管理使用了Spring Security,用戶登錄成功之后,Spring Security幫你把用戶信息保存在Session里,但是具體保存在哪里,要是不深究你可能就不知道,今天小編就帶大家具體了解一下Spring Security實(shí)現(xiàn)用戶名密碼登錄的流程2021-11-11
Spring的@Value如何從Nacos配置中心獲取值并自動(dòng)刷新
這篇文章主要介紹了Spring的@Value如何從Nacos配置中心獲取值并自動(dòng)刷新,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
springboot集成shiro自定義登陸過(guò)濾器方法
這篇文章主要介紹了springboot集成shiro自定義登陸過(guò)濾器方法,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
java中字符進(jìn)行全角半角轉(zhuǎn)換示例代碼
全角:指一個(gè)字符占用兩個(gè)標(biāo)準(zhǔn)字符位置,而半角:指一字符占用一個(gè)標(biāo)準(zhǔn)的字符位置,在日常開(kāi)發(fā)中經(jīng)常會(huì)遇到全角半角轉(zhuǎn)換的要求,下面這篇文章主要給大家介紹了關(guān)于java中字符進(jìn)行全角半角轉(zhuǎn)換的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-08-08最新評(píng)論

