Express socket.io to netty socket.io in spring - 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.

Related

RXStomp WebSocket Client not receiving data sometimes

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?

websocket_sockJS : all transport error. after adding { transports: ['websocket']}

background:
whenever I open WebSocket page
I had a few XHR_SEND? 404 - error but finally XHR_SEND? got success response and connected to WebSocket.
So to avoid this 404 error, I decide to use WebSocket only. so I added this
: return new SockJS(connectionUrl,, null, { transports: ['websocket']});
then now..
XHR_SEND? are gone but it doesn't connect to server at all.
+FYI: I have 2 servers ..(i think because of this previously I got XHR_send error. )
The below screenshot is repeating. but never connected
JAVA
#Configuration
#EnableWebSocketMessageBroker
public class BatchSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/batch-socket");
registry.addEndpoint("/batch-socket").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/socketio/");
}
ANGULAR7
import { Injectable, OnDestroy, Inject, Optional } from '#angular/core';
import * as SockJS from '../../../assets/lib/sockjs.min.js';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter, first, switchMap } from 'rxjs/operators';
import { StompSubscription, Stomp, Client, Frame, Message, StompConfig, Versions } from '#stomp/stompjs';
#Injectable({
providedIn: 'root'
})
export class SocketService {
private client: Client;
private state: BehaviorSubject<any>;
private baseUrl: any = "/" + window.location.href.substr(0).split('/')[3] + "/";
constructor() {
}
init() {
let connectionUrl = this.baseUrl + "batch-socket";
console.log("MY URL is " + connectionUrl);
return new Promise((resolve, reject) => {
let config = new StompConfig();
config.heartbeatOutgoing = 10000;
config.heartbeatIncoming = 10000;
config.stompVersions = new Versions(['1.0', '1.1']);
config.webSocketFactory = function () {
return new SockJS(connectionUrl, null, { transports: ['websocket']});
//PREVIOUS : return new SockJS(connectionUrl)
}
config.debug = function (str) {
console.log("#socketDebug: " + str)
}
this.client = new Client();
this.client.configure(config);
console.log(this.client);
console.log("#socketSvc: starting connection...");
const _this = this;
this.client.onConnect = function (frame) {
console.log("#socketSvc: connection established.");
console.log(frame);
_this.state = new BehaviorSubject<any>(SocketClientState.ATTEMPTING);
_this.state.next(SocketClientState.CONNECTED);
resolve(frame.headers['user-name']);
}
this.client.onWebSocketClose = function (msg){
console.log("#socketSvc: connection closed.");
console.log(msg);
}
this.client.onWebSocketError = function(msg){
console.log("#socketSvc: connection error.");
console.log(msg);
}
this.client.onDisconnect = function(msg){
console.log("#socketSvc: socket disconnected.");
console.log(msg);
//this.init();
}
this.client.onStompError = function(msg){
console.log("#socketSvc: stomp error occurred.");
console.log(msg);
}
this.client.activate();
});
}
private connect(): Observable<Client> {
return new Observable<Client>(observer => {
this.state.pipe(filter(state => state === SocketClientState.CONNECTED)).subscribe(() => {
observer.next(this.client);
});
});
}
onPlainMessageReceived(topic: string): Observable<string> {
return this.onMessageReceived(topic, SocketService.textHandler);
}
onMessageReceived(topic: string, handler = SocketService.jsonHandler): Observable<any> {
return this.connect().pipe(first(), switchMap(client => {
return new Observable<any>(observer => {
const subscription: StompSubscription = client.subscribe(topic, message => {
observer.next(handler(message));
});
return () => client.unsubscribe(subscription.id);
});
}));
}
static jsonHandler(message: Message): any {
return JSON.parse(message.body);
}
static textHandler(message: Message): string {
return message.body;
}
disconnect() {
this.connect().pipe(first()).subscribe(client => client.deactivate());
this.client.deactivate();
}
}
export enum SocketClientState {
ATTEMPTING, CONNECTED
}
I found a reason for this issue.
I realized that I have 2 war files.
hence one has my code(socket connection) , the other one doesnt have a code (socket connection).
so it throws the error.
=> resolved by removing the war file that doesn't have socket connection.

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"

Angular2/Websocket: how to return an observable for incoming websocket messages

I'm going to use Angular2 to receive websocket incoming messages and update a webpage based on those received messages. Right now, I'm using a dummy echo websocket service and will replace it.
From my understanding, the function which receive websocket messages has to return an observable that is subscribed by a handler who will update the webpage. But I can't figure out how to return an observable.
Code snippet is attached below. The MonitorService creates a websocket connection and return an observable containing the received messages.
#Injectable()
export class MonitorService {
private actionUrl: string;
private headers: Headers;
private websocket: any;
private receivedMsg: any;
constructor(private http: Http, private configuration: AppConfiguration) {
this.actionUrl = configuration.BaseUrl + 'monitor/';
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
}
public GetInstanceStatus = (): Observable<Response> => {
this.websocket = new WebSocket("ws://echo.websocket.org/"); //dummy echo websocket service
this.websocket.onopen = (evt) => {
this.websocket.send("Hello World");
};
this.websocket.onmessage = (evt) => {
this.receivedMsg = evt;
};
return new Observable(this.receivedMsg).share();
}
}
Below is another component which subscribes to the observable returned from above and updates webpages correspondingly.
export class InstanceListComponent {
private instanceStatus: boolean
private instanceName: string
private instanceIcon: string
constructor(private monitor: MonitorService) {
this.monitor.GetInstanceStatus().subscribe((result) => {
this.setInstanceProperties(result);
});
}
setInstanceProperties(res:any) {
this.instanceName = res.Instance.toUpperCase();
this.instanceStatus = res.Status;
if (res.Status == true)
{
this.instanceIcon = "images/icon/healthy.svg#Layer_1";
} else {
this.instanceIcon = "images/icon/cancel.svg#cancel";
}
}
}
Now, I'm running into this error in the browser console
TypeError: this._subscribe is not a function
I put it on a plunker and I added a function for sending message to the Websocket endpoint. Here is the important edit:
public GetInstanceStatus(): Observable<any>{
this.websocket = new WebSocket("ws://echo.websocket.org/"); //dummy echo websocket service
this.websocket.onopen = (evt) => {
this.websocket.send("Hello World");
};
return Observable.create(observer=>{
this.websocket.onmessage = (evt) => {
observer.next(evt);
};
})
.share();
}
Update
As you mentioned in your comment, a better alternative way is to use Observable.fromEvent()
websocket = new WebSocket("ws://echo.websocket.org/");
public GetInstanceStatus(): Observable<Event>{
return Observable.fromEvent(this.websocket,'message');
}
plunker example for Observable.fromEvent();
Also, you can do it using WebSocketSubject, although, it doesn't look like it's ready yet (as of rc.4):
constructor(){
this.websocket = WebSocketSubject.create("ws://echo.websocket.org/");
}
public sendMessage(text:string){
let msg = {msg:text};
this.websocket.next(JSON.stringify(msg));
}
plunker example
Get onMessage data from socket.
import { Injectable } from '#angular/core';
import {Observable} from 'rxjs/Rx';
#Injectable()
export class HpmaDashboardService {
private socketUrl: any = 'ws://127.0.0.0/util/test/dataserver/ws';
private websocket: any;
public GetAllInstanceStatus(objStr): Observable<any> {
this.websocket = new WebSocket(this.socketUrl);
this.websocket.onopen = (evt) => {
this.websocket.send(JSON.stringify(objStr));
};
return Observable.create(observer => {
this.websocket.onmessage = (evt) => {
observer.next(evt);
};
}).map(res => res.data).share();
}
**Get only single mesage from socket.**
public GetSingleInstanceStatus(objStr): Observable<any> {
this.websocket = new WebSocket(this.socketUrl);
this.websocket.onopen = (evt) => {
this.websocket.send(JSON.stringify(objStr));
};
return Observable.create(observer => {
this.websocket.onmessage = (evt) => {
observer.next(evt);
this.websocket.close();
};
}).map(res => res.data).share();
}
}
A different approach I used is with subject:
export class WebSocketClient {
private client: WebSocket | undefined;
private subject = new Subject<string>();
...
private connect() {
const client = new WebSocket(fakeUrl);
const client.onmessage = (event) => {
this.subject.next(event.data);
};
}
private watch() { return this.subject } // can be mapped
}
And using it will be in my opinion clearer:
const client = new WebSocketClient(); // can also be injected
client.connect();
client.watch().subscribe(x => ...);
Happy coding!

Resources