import {Injectable} from '@angular/core'; import {Setting} from './setting'; import {ChatMessage} from './chat.message'; import {SocketRegistrationMessage} from './socket.registration.message'; import {SocketReceivedChatMessage} from './socketReceivedChatMessage'; import {WebsocketListener} from './websocket.listener'; import {SocketSendMessage} from './socket.send.message'; import {SocketKeepaliveMessage} from './socket.keepalive.message'; @Injectable({ providedIn: 'root' }) export class WebsocketService { private socket: WebSocket = new WebSocket(Setting.WEBSOCKET); private userList: Map = new Map(); private listener: WebsocketListener; private chatToken: string; private isReconnectDesired = true; private lastReconnectAttempts: Date[] = []; initializeSocket(chatToken: string): void { this.chatToken = chatToken; this.socket = new WebSocket(Setting.WEBSOCKET); this.socket.addEventListener('open', () => {this.authorize(); this.listener.onConnection(); }); this.socket.addEventListener('message', (transmission: MessageEvent) => {this.onMessage(transmission); }); this.socket.addEventListener('close', () => {this.onClose(); }); this.socket.addEventListener('error', () => {this.onError(); }); } destroy(): void { this.socket = null; } setListener(listener: WebsocketListener): void { this.listener = listener; } sendChatMessage(message: string): void { const socketMessage: SocketSendMessage = { type: Response.CHAT_MESSAGE, message }; this.socket.send(JSON.stringify(socketMessage)); } sendKeepAliveMessage(): void { if (this.socket === null) { return; } const socketMessage: SocketKeepaliveMessage = { type: Response.KEEP_ALIVE }; this.socket.send(JSON.stringify(socketMessage)); } private authorize(): void { const message: SocketRegistrationMessage = {type: Response.REGISTRATION_MESSAGE, token: this.chatToken}; this.socket.send(JSON.stringify(message)); } private onMessage(transmission: MessageEvent): void { const response = JSON.parse(transmission.data); switch (response.type) { case Response.CHAT_MESSAGE: const messageReceived: SocketReceivedChatMessage = response; const message: ChatMessage = { id: messageReceived.id, userId: messageReceived.userId, username: this.userList.get(messageReceived.userId), datetime: messageReceived.datetime, message: messageReceived.message, htmlMessage: '', }; this.listener.onChatMessage(message); break; case Response.REGISTRATION_MESSAGE: this.userList.set(response.userId, response.username); break; case Response.USERLIST: response.users.forEach( (user) => { this.userList.set(user.userId, user.username); } ); break; default: throw new Error('Unknown message type: ' + response.type); } } private reconnect(): void { this.initializeSocket(this.chatToken); this.authorize(); this.listener.onReconnect(); } private needsAuthorizationForFurtherReconnectAttempts(): boolean { if (this.lastReconnectAttempts.length < 3) { return false; } const now = new Date().getTime(); let recentAttempts = 0; for (const attempt of this.lastReconnectAttempts) { if (now - attempt.getTime() < 20000) { recentAttempts++; } } return recentAttempts >= 3; } private onError(): void { if (this.needsAuthorizationForFurtherReconnectAttempts()) { this.lastReconnectAttempts = []; this.isReconnectDesired = false; setTimeout( () => { this.isReconnectDesired = true; this.onClose(); }, 10000 ); } if (this.isReconnectDesired) { this.lastReconnectAttempts.push(new Date()); } this.listener.onError('Die Verbindung konnte nicht hergestellt werden!'); } private onClose(): void { this.listener.onError('Verbindung unterbrochen!'); if (this.isReconnectDesired) { this.reconnect(); } } } enum Response { CHAT_MESSAGE = 1, REGISTRATION_MESSAGE, USERLIST, KEEP_ALIVE }