goldenChat base source add
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 차트·실시간 전략용 STOMP 단일 연결 (SockJS 1회).
|
||||
* 훅마다 StompClient 를 만들면 "closed before connection is established" 가 반복된다.
|
||||
*/
|
||||
import { Client as StompClient, IMessage, StompSubscription } from '@stomp/stompjs';
|
||||
import SockJS from 'sockjs-client';
|
||||
import { getStompSockJsUrl } from './backendApi';
|
||||
|
||||
type MessageHandler = (msg: IMessage) => void;
|
||||
|
||||
interface TopicEntry {
|
||||
handlers: Set<MessageHandler>;
|
||||
stompSub?: StompSubscription;
|
||||
}
|
||||
|
||||
let client: StompClient | null = null;
|
||||
let connected = false;
|
||||
const topics = new Map<string, TopicEntry>();
|
||||
let disconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function handlerCount(): number {
|
||||
let n = 0;
|
||||
for (const entry of topics.values()) n += entry.handlers.size;
|
||||
return n;
|
||||
}
|
||||
|
||||
function cancelPendingDisconnect(): void {
|
||||
if (disconnectTimer) {
|
||||
clearTimeout(disconnectTimer);
|
||||
disconnectTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function resubscribeAll(): void {
|
||||
if (!client?.connected) return;
|
||||
for (const [topic, entry] of topics) {
|
||||
if (entry.handlers.size === 0) continue;
|
||||
entry.stompSub?.unsubscribe();
|
||||
entry.stompSub = client.subscribe(topic, msg => {
|
||||
entry.handlers.forEach(h => h(msg));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getOrCreateClient(): StompClient {
|
||||
if (client) return client;
|
||||
|
||||
client = new StompClient({
|
||||
webSocketFactory: () => new SockJS(getStompSockJsUrl()),
|
||||
reconnectDelay: 5000,
|
||||
heartbeatIncoming: 10_000,
|
||||
heartbeatOutgoing: 10_000,
|
||||
onConnect: () => {
|
||||
connected = true;
|
||||
cancelPendingDisconnect();
|
||||
resubscribeAll();
|
||||
},
|
||||
onDisconnect: () => {
|
||||
connected = false;
|
||||
for (const entry of topics.values()) {
|
||||
entry.stompSub = undefined;
|
||||
}
|
||||
},
|
||||
onStompError: frame => {
|
||||
console.warn('[stompChartBroker] STOMP error', frame.headers?.message ?? frame);
|
||||
},
|
||||
onWebSocketError: ev => {
|
||||
console.warn('[stompChartBroker] WebSocket error', getStompSockJsUrl(), ev);
|
||||
},
|
||||
onWebSocketClose: () => {
|
||||
connected = false;
|
||||
for (const entry of topics.values()) {
|
||||
entry.stompSub = undefined;
|
||||
}
|
||||
},
|
||||
});
|
||||
return client;
|
||||
}
|
||||
|
||||
function ensureActive(): void {
|
||||
cancelPendingDisconnect();
|
||||
const c = getOrCreateClient();
|
||||
if (!c.active) c.activate();
|
||||
}
|
||||
|
||||
function scheduleDisconnect(): void {
|
||||
cancelPendingDisconnect();
|
||||
disconnectTimer = setTimeout(() => {
|
||||
disconnectTimer = null;
|
||||
if (handlerCount() > 0) return;
|
||||
if (!client?.active) return;
|
||||
// 연결 수립 중 deactivate 하면 "closed before connection is established" 발생
|
||||
if (!connected) {
|
||||
scheduleDisconnect();
|
||||
return;
|
||||
}
|
||||
client.deactivate();
|
||||
connected = false;
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
/**
|
||||
* STOMP 토픽 구독 (참조 카운트). 반환 함수로 해제.
|
||||
*/
|
||||
export function subscribeStompTopic(topic: string, handler: MessageHandler): () => void {
|
||||
let entry = topics.get(topic);
|
||||
if (!entry) {
|
||||
entry = { handlers: new Set() };
|
||||
topics.set(topic, entry);
|
||||
}
|
||||
entry.handlers.add(handler);
|
||||
|
||||
if (connected && client && entry.handlers.size === 1) {
|
||||
entry.stompSub = client.subscribe(topic, msg => {
|
||||
entry!.handlers.forEach(h => h(msg));
|
||||
});
|
||||
}
|
||||
|
||||
ensureActive();
|
||||
|
||||
return () => {
|
||||
const e = topics.get(topic);
|
||||
if (!e) return;
|
||||
e.handlers.delete(handler);
|
||||
if (e.handlers.size === 0) {
|
||||
e.stompSub?.unsubscribe();
|
||||
topics.delete(topic);
|
||||
scheduleDisconnect();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function isStompChartConnected(): boolean {
|
||||
return connected && Boolean(client?.connected);
|
||||
}
|
||||
Reference in New Issue
Block a user