metasocket-cordova/src/app/websocket.service.ts

162 lines
4.2 KiB
TypeScript

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<number, string> = new Map<number, string>();
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
}