RXStomp WebSocket Client not receiving data sometimes - websocket

Here is my configuration for RXStomp on frontend;
import { RxStompConfig } from '#stomp/rx-stomp';
import { environment } from '../../../environments/environment';
export const myRxStompConfig: RxStompConfig = {
reconnectDelay: 20000,
debug: (msg: string): void => {
if (!environment.production) {
console.log(msg);
}
},
};
Here is the code for the rxStompService;
import { Injectable } from "#angular/core";
import { RxStomp } from "#stomp/rx-stomp";
import SockJS from "sockjs-client";
import { environment } from "../../../environments/environment";
import { myRxStompConfig } from "../configurations/rx-stomp.config";
#Injectable({
providedIn: "root",
})
export class RxStompService extends RxStomp {
public currentRetry = 0;
public resetRetry() {
this.currentRetry = 0;
}
}
export function rxStompServiceFactory() {
const rxStomp = new RxStompService();
myRxStompConfig.webSocketFactory = function () {
return new SockJS(`${environment.baseUrl}/public/chatsocket`);
};
rxStomp.resetRetry();
myRxStompConfig.beforeConnect = (): Promise<void> => {
return new Promise<void>((resolve, reject) => {
if (rxStomp.currentRetry <= 5) {
rxStomp.currentRetry++;
resolve();
}
});
};
rxStomp.configure(myRxStompConfig);
rxStomp.activate();
return rxStomp;
}
The websocket was working fine until a couple of days ago when the socket broke on the production server and then we changed the URL of the websocket on the backend from public/websocket to public/chatsocket. The socket gets connected and then I subscribe to the required channel using
this.rxStompService.watch(`/user/${id}/message`).subscribe((message) => {
console.log(message.body);
};
The messages send correctly on the backend (Springboot) using this code;
private void sendWebMessageToConversation(MessageResponseDto messageResponseDto, String destinationId) {
try{
simpMessagingTemplate.convertAndSendToUser(destinationId, "/message", messageResponseDto);
}
catch (Exception e){
logger.error(e.getMessage(), e);
}
}
After debugging we found that the message gets filtered correctly and sent to all the users in a particular conversation via simpMessagingTemplate.convertAndSendToUser but the message does not get received on the frontend socket client RXStomp, even though the socket connection is established and the client is subscribed/watching the correct channel.
This feature works correctly on all of the test environments but for some reason there is an inconsistency when working on the production server. Sometimes one user subscribed to the channel will receive messages but the other won't. Or both users won't receive messages, or the feature works correctly and both receive messages properly. How do I fix this to make it work correctly all the time?

Related

Express socket.io to netty socket.io in spring

I need to stream data from my backend (in spring) to my angular frontend.
I cant get the netty socket.io implementation working.
public ConnectListener onUserConnectWithSocket = new ConnectListener() {
#Override
public void onConnect(SocketIOClient socketIOClient) {
log.info("Client connected: " + socketIOClient.getSessionId());
socketIOClient.sendEvent("getAllDashboardData", generateRandomValues());
}
};
public DataListener<String> getAllDashboardData = new DataListener<String>() {
#Override
public void onData(SocketIOClient socketIOClient, String message, AckRequest ackRequest) throws Exception {
log.info("Message received: " + message);
socketIOClient.sendEvent("getAllDashboardData", generateRandomValues().toString());
}
};
when i have something like this, the EventListener never gets called (does not log User requested data). Hence, the onConnect logs that the frontend connected.
I tried out the frontend call using express!
This simple examples works perfect:
module.exports = (io) => {
io.on('connect', (socket) => {
console.log('user connected');
socket.on('getAllDashboardData', (data) => {
//send some data to client back
socket.emit('getAllDashboardData', {data: 'data'});
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
}
how could i write this in spring?
I also tested the backend with postman and it works fine!
The answer is:
import { Injectable } from '#angular/core';
const io = require('socket.io-client');
import {Observable} from "rxjs";
#Injectable({
providedIn: 'root'
})
export class SocketService {
socket: any;
readonly uri = 'ws://localhost:8085';
constructor() {
this.socket = io(this.uri);
}
listen(eventName: string) {
return new Observable((resolve) => {
this.socket.on(eventName, (data: any) => {
// this.socket.emit(eventName, data); maybe don't use this produces 1000 of calls
resolve.next(data);
});
});
}
emit(eventName: string, data: any) {
this.socket.emit(eventName, data);
}
}
and use socket.io-client version 2.3.0 to work with netty spring.

Deno: How to use WebSocket with oak?

As Deno was released last Wednesday, I tried to play with it and redo the little example Chat App, I tried this:
import { Application, Router, send } from 'https://deno.land/x/oak/mod.ts';
import { listenAndServe } from 'https://deno.land/std/http/server.ts'
const app = new Application();
const router = new Router();
router
.get('/ws', handleSocket);
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: HTTP_PORT });
app.ts
import { WebSocket, acceptWebSocket, isWebSocketCloseEvent, acceptable } from 'https://deno.land/std/ws/mod.ts'
import { v4 } from 'https://deno.land/std/uuid/mod.ts'
const users = new Map<string, WebSocket>()
export const handleSocket = async (ctx: any) => {
if (acceptable(ctx.request.serverRequest)) {
const { conn, r: bufReader, w: bufWriter, headers } = ctx.request.serverRequest;
const socket = await acceptWebSocket({
conn,
bufReader,
bufWriter,
headers,
});
await socketEventHandlers(socket);
} else {
throw new Error('Error when connecting websocket');
}
}
...
export const socketEventHandlers = async (ws: WebSocket): Promise<void> => {
// Register user connection
const userId = v4.generate()
users.set(userId, ws)
await broadcast(`> User with the id ${userId} is connected`)
// Wait for new messages
for await (const event of ws) {
const message = typeof event === 'string' ? event : ''
await broadcast(message, userId)
// Unregister user conection
if (!message && isWebSocketCloseEvent(event)) {
users.delete(userId)
await broadcast(`> User with the id ${userId} is disconnected`)
}
}
}
socket.ts
The websocket connection works perfectly with the import { listenAndServe } from 'https://deno.land/std/http/server.ts'
, but with the code above I got errors like WebSocket connection to 'ws://localhost:3000/ws' failed: Invalid frame header.
Does anybody have any tips to solve it? Thx ;)
TL;DR - This has been updated since answer was accepted and is much simpler now.
router.get('/ws', async ctx => {
const sock = await ctx.upgrade();
handleSocket(sock);
});
Credit https://github.com/oakserver/oak/pull/137
The issue happens because you're using the wrong version of the libraries. Always use versioned URLs in Deno.
For Deno 1.0.0, you'll need to use oak v4.0.0 & std v0.51.0
app.ts
import { Application, Router, send } from 'https://deno.land/x/oak#v4.0.0/mod.ts';
socket.ts
import { WebSocket, acceptWebSocket, isWebSocketCloseEvent, acceptable } from 'https://deno.land/std#0.51.0/ws/mod.ts'
import { v4 } from 'https://deno.land/std#0.51.0/uuid/mod.ts'
Once you make those changes, you'll be able to connect correctly to the WebSocket Server.
const ws = new WebSocket("ws://127.0.0.1:8080/ws")
ws.onopen = function () {
ws.send('OAK is working!')
}

Reconnect WebSocket in Angular 5 with Rxjs observable and observer

In any case, if my application got disconnected from WebSocket I am not able to reconnect it. I am attaching the sample code please suggest me the idea to how I can reconnect WebSocket and initialize my identity on web socket server again.
I have made my application with the help of this tutorial.
https://tutorialedge.net/typescript/angular/angular-websockets-tutorial/
I have written the same code in my application except for my application requirement.
The tutorial that I have been followed does not have the feature to reconnect the WebSocket in any case like internet break or by some reason our WebSocket server got restart because I am running my WebSocket server with SupervisorD and it will automatically restart if WebSocket server get to stop in any case
My application is in production and many customers are using now so I can not change all flow and recreate the code for WebSocket in this application.
I am adding all code that I am using
websocket.service.ts
import { Injectable } from '#angular/core';
import * as Rx from 'rxjs/Rx';
#Injectable()
export class WebsocketService {
connected: boolean = false;
initialized: boolean= false;
constructor() { }
private subject: Rx.Subject<MessageEvent>;
public connect(url): Rx.Subject<MessageEvent> {
if (!this.subject) {
this.subject = this.create(url);
// console.log("Successfully connected: " + url);
}
return this.subject;
}
private create(url): Rx.Subject<MessageEvent> {
let ws = new WebSocket(url);
// here i am trying to reconnect my websocket
// setInterval (function () {
// if (ws.readyState !== 1) {
// ws = new WebSocket(url);
// this.initialized = false;
// }
// console.log(this.initialized);
// if (ws.readyState == 1 && this.initialized == false) {
// ws.send('{"type":"add",
"t":"14bfa6xxx", "from_numbers":
["xxxx","xxxxx"], "platform":"xxxx"}');
// this.initialized = true;
// }
// console.log(this.initialized);
// }, 4000);
let observable = Rx.Observable.create(
(obs: Rx.Observer<MessageEvent>) => {
ws.onmessage = obs.next.bind(obs);
ws.onerror = obs.error.bind(obs);
ws.onclose = obs.complete.bind(obs);
return ws.close.bind(ws);
})
let observer = {
next: (data: Object) => {
if (ws.readyState === WebSocket.OPEN) {
if (data['type'] == 'add') {
console.log("Connection Initialized");
}
ws.send(JSON.stringify(data));
}
}
}
return Rx.Subject.create(observer, observable);
}
}
Chat.service.ts
import { Injectable } from '#angular/core';
import { Observable, Subject } from 'rxjs/Rx';
import { WebsocketService } from './websocket.service';
#Injectable()
export class ChatService {
public messages: Subject<Message>;
constructor(wsService: WebsocketService, private authService: AuthService) {
this.messages = <Subject<Message>>wsService
.connect(socket_url)
.map((response: MessageEvent): Message => {
const data = JSON.parse(response.data);
console.log(data);
return data;
});
}
}
and finality I have used this in our component to subscribe message.
constructor(private chatService: ChatService,) {
this.socketMessages();
}
socketMessages() {
this.chatService.messages.subscribe(msg => {
console.log(msg)
});
}

How to get the socket-id in Angular using Socket-io.client

In my angular app I am using socket.io-client npm package to make a socket-io communication to another node-server.
I have the following code forthe same.
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import * as io from 'socket.io-client';
#Injectable({
providedIn: 'root'
})
export class DataService {
constructor() { }
private url = 'http://localhost:3000';
private socket;
getLiveData1() {
let observable = new Observable(observer => {
this.socket = io(this.url);
console.log("THIS SOCKET IS:getLiveData1");
this.socket.on('connect', function() {
console.log("on connect:THIS SOCKET IS id is");
console.log(this.socket.id);
console.log(this.socket.socket.id);
});
this.socket.on('message', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
}
})
return observable;
}
I am trying to access the client id only on the connect event.
this.socket.on('connect', function() {
console.log("on connect:THIS SOCKET IS id is");
console.log(this.socket.id);
console.log(this.socket.socket.id);
});
however both the log-statements where i am trying to log the socket-id using : this.socket.id or this.socket.socket.id errors out saying that this.socket is undefined
How can i get the client-side socket-id in this case?
From docs
https://socket.io/docs/client-api/#socket-id
socket.id
(String)
An unique identifier for the socket session. Set after the connect event is triggered, and updated after the reconnect event.
const socket = io('http://localhost');
console.log(socket.id); // undefined
socket.on('connect', () => {
console.log(socket.id); // 'G5p5...'
});
You doing it right, your problem that you are using es5 function, that doesn't keep this context. Replace it with arrow functions. Or bind context.
this.socket.on('connect', /* arrow function */() => {
console.log("on connect:THIS SOCKET IS id is");
console.log(this.socket.id);
});
this worked for me (Angular 6)
ngOnInit(): void {
this.socket = io.connect('http://localhost:5000');
this.socket.on('connect', this.socketOnConnect)}
above is the initialization code for socket and binding an angular method to "ON CONNECT" method
socketOnConnect() {
console.log('connected');
var socket = this;
console.log(socket['id']); } // prints socket id
the socket_on_connect method has a scope of Socket itself, so if we use this inside the method, it displays socket object. hence the above method works
npms used
"#types/socket.io-client": "^1.4.32"
"socket.io-client": "^2.3.0"

Handling Nest.js app on Redis cache store disconnection

When I setup Redis as cache store like:
import { Module, CacheModule } from '#nestjs/common';
import * as redisStore from 'cache-manager-redis-store';
#Module({
imports: [
CacheModule.register({
store: redisStore,
host: 'localhost',
port: 6379,
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
It works as expected. However, if for some reason the connection to Redis goes down, the whole application will crash:
Error: Redis connection to localhost:6379 failed - connect ECONNREFUSED
127.0.0.1:6379
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1113:14)
How can I gracefully handle these errors or even try to reconnect?
Ok, found it myself. I need to inject the exposed CACHE_MANAGER in the module constructor to get my hands on the client:
import { CACHE_MANAGER, Inject } from '#nestjs/common';
...
export class AppModule {
constructor(#Inject(CACHE_MANAGER) cacheManager) {
const client = cacheManager.store.getClient();
client.on('error', (error) => {
console.info(error);
});
}
}
you can handle or you can check connections whether is connected or not. you can also use nestjs/bull module Click here
Redis Service Ready = queue.clients[0].status === 'ready'
Redis Service Disconnect = queue.clients[0].status === 'reconnecting'
import { InjectQueue } from '#nestjs/bull';
import { BadRequestException, HttpException, HttpStatus, Injectable, Logger } from '#nestjs/common';
import { Queue } from 'bull';
import CONFIG from 'src/config/config';
import { CreateEmailServiceDto } from './dto/create-email-service.dto';
#Injectable()
export class EmailServiceService {
private readonly logger = new Logger(this.constructor.name);
constructor(
#InjectQueue(CONFIG.REDIS_QUEUE_NAME)
private mailQueue: Queue,
) {
}
async sendConfirmationEmail(user: CreateEmailServiceDto): Promise<any> {
try {
const queue: any = this.mailQueue;
if (queue.clients[0].status === 'reconnecting') {
throw new HttpException("Redis Service is unavailable!",
HttpStatus.SERVICE_UNAVAILABLE);
}
await this.mailQueue
.add('confirmation', {
user,
});
return {
message: 'Email Sent Successfully!',
status: 201,
};
} catch (error) {
console.log(error);
this.logger.error(`Error queueing confirmation email to user
${user.to}`);
if (error?.response && error?.status)
throw new HttpException(error.response, error.status);
throw new BadRequestException(error);
}
}
}
You can use the redisCache class as follows
redisCache.set('foo', 'bar', { ttl: ttl }, (err) => {
if (err) {
throw err;
}
redisCache.get('foo', (err, result) => {
console.log(result);
// >> 'bar'
redisCache.del('foo', (err) => {
});
});
});

Resources