How to notify client of successful/failed subscription? - autobahn

I'm subscribing to 'queue:1.2.3'. How does the client know if they have successfully subscribed? Is there a mechanism for the server to respond to the client?
Below is a simple program that I'm using to test the subscriptions. I'm not sure what to show on the server side.
var sess;
var wsuri = 'ws://test.lan:8000';
window.onload = function() {
// connect to WAMP server
ab.connect(wsuri,
// WAMP session was established
function (session) {
sess = session;
console.log("Connected to " + wsuri);
},
// WAMP session is gone
function (code, reason) {
sess = null;
console.log("Connection lost (" + reason + ")");
}
);
};
function subscribe()
{
sess.subscribe("queue:1.2.3", topicReceived)
}
function topicReceived(topicUri, event)
{
console.log(topicUri + ' ' + event)
}
I feel like this post touches on my question: Calling dispatch in subscribe in Autobahn

Not in WAMPv1. A acknowledgement (with subsription ID) or a subscribe error (with error URI) will be part of WAMPv2 which is currently in the cooking.

Related

MVC CORE SignalR send messages specific user only

Im using SignalR Core and ASPNET Core 2.2 and try to to send message to single user. for all user it's working fine.
Csharp code
[HubMethodName("SendMessageToUser")]
public Task DirectMessage(string user, string message)
{
//eg.user = abcd#gmail.com
return Clients.User(user).SendAsync("ReceiveMessage", message);
}
JS code
connection.on("ReceiveMessage", function (user, message) {
var msg = message.replace(/&/g, "&").replace(/</g,"<").replace(/>/g, ">");
var encodedMsg = user + " says " + msg;
var li = document.createElement("li");
li.textContent = encodedMsg;
document.getElementById("messagesList").appendChild(li);
});
You can send message to specific user using their connection id so
I need to create a hub like this. The GetConnectionId will return the connect id of that user when they connect to signalR hub
public class ConnectionHub : Hub
{
public async Task Send(string userId)
{
var message = $"Send message to you with user id {userId}";
await Clients.Client(userId).SendAsync("ReceiveMessage", message);
}
public string GetConnectionId()
{
return Context.ConnectionId;
}
}
Then in startup.cs
services.AddSignalR();
app.UseSignalR(routes =>
{
routes.MapHub<ConnectionHub>("/connectionHub");
});
Then the client side code
(function () {
var connection = new signalR.HubConnectionBuilder().withUrl("/connectionHub").build();
connection.start().then(function () {
console.log("connected");
connection.invoke('getConnectionId')
.then(function (connectionId) {
sessionStorage.setItem('conectionId', connectionId);
// Send the connectionId to controller
}).catch(err => console.error(err.toString()));;
});
$("#sendmessage").click(function () {
var connectionId = sessionStorage.getItem('conectionId');
connection.invoke("Send", connectionId);
});
connection.on("ReceiveMessage", function (message) {
console.log(message);
});
})();
First I invoke getConnectionId in the hub to get user connection id then I can send message to specific user using their connection id.
Hope that help

Websockets+Single instance+crosstab communication best way?

I'm utilizing websockets for passing json messages but I don't want multiple ws connections if multiple tabs are open.
To reduce the connections I want to implement a single ws connection object that can send/receive messages to and from all tabs to my website. The object should forward the json to all tabs and each tab will process the message.
I've been looking at web/shared/service workers and I'm not sure the 2018 path to solve the issue and browser support seems to be a concern as well.
Looks like shared workers are not supported in Safari in support of service workers. Chrome/ff/opera seem to support shared workers.
In short it's a little confusing, bit of a mess and I want to know the best path forward with the best support.
If you know of a good resource, example code to implement ws with your suggested method please provide it as well.
After further research I've decided to implement web workers.
At this point I'm having success and I wanted to add an important piece that I got stuck on for future readers.
In my worker.js file I put this at the top to kick things off. The importScripts function threw an error if I didn't do it otherwise.
Also for the sake of helping, this is my skeleton code in my worker.js file that works. Message processing from the html pages are separated from the ws messages received from the server. You can start, stop the worker from the html page.
All tabs will get the messages from the worker, each page needs to process the messages as needed.
I'm also using robust-websockets so it auto reconnects from this github as this code works with web workers and is maintained. There is another project by the same name that isn't as updated by the time of this post. The reconnecting-websockets does not support web workers and you will get an error. - https://github.com/nathanboktae/robust-websocket
html
<script>
document.addEventListener('DOMContentLoaded', init);
function init(){
worker = new Worker('js/ws_worker.js');
worker.addEventListener('message', workerMessaged);
worker.postMessage({ "args": '<username_admin>', "cmd": 'start', "url": '127.0.0.1:8000' });
worker.postMessage({ "message": 'Initialize new worker'});
console.log('Message posted to worker, start');
}
// Received a json message from the Worker, process it.
function workerMessaged(ev){
console.log('Message received from worker');
console.log(ev.data);
worker.postMessage({ "cmd": 'message', "message": 'Sending reply over ws'});
}
worker.js
// proper initialization
if( 'function' === typeof importScripts) {
importScripts('robust-websocket.js');
var WebSocket;
self.addEventListener("message", function(e) {
var args = e.data.args;
var cmd = e.data.cmd;
var roomName = e.data.args;
var url = e.data.url;
var message = e.data;
// Code to process ws messages from the server
WebSocket.onmessage = function(event) {
console.log(" WebSocket message received: " + event.data, event);
self.postMessage(event.data);
};
WebSocket.onerror = function(event) {
console.log(" WebSocket message received: " + event.data, event);
self.postMessage(event.data);
};
if (cmd === 'start') {
WebSocket = new RobustWebSocket(
'ws://' + url +
'/ws/' + roomName + '/');
console.log('Connected via websockets');
/* Send initial message to open the connection and finalize the ws object*/
WebSocket.onopen = function() {
var obj = { "message": "hello" };
var json = JSON.stringify(obj);
WebSocket.send(json);
};
} else if (cmd === 'stop') {
WebSocket.onclose = function() {
console.log('Closing WebSocket');
WebSocket.close();
console.log('Closed WebSocket');
console.log('Terminating Worker');
self.close(); // Terminates the worker.
};
} else if (cmd === 'message') {
WebSocket.onopen = function() {
var json = JSON.stringify(message);
WebSocket.send(json);
};
console.log('message sent over websocket');
console.log('message');
} else {
console.log('logging error ' + e.data);
console.log(e.data);
self.postMessage('Unknown command: ');
}
}, false);
};

How to handle Stomp JS disconnect in Spring MVC?

In the project I've been working on, I found a problem with Stomp JS client disconnect.
It's about a case when user closes the tab in browser or when there's a problem with internet connection. In other words, I want to be able to handle this disconnect java side (server side). Is it possible?
I already have code that handles Websocket opening and it works fine, what I want to achieve is to handle connection closing (both expected and unexpected cases)
Here's a piece of my HTML file that includes Stomp JS related code (I can provide Java code as well if it's necessary):
<script type="text/javascript">
$(document).ready(function() {
connect();
});
var stompClient = null;
function connect() {
var socket = new SockJS('/lock-document');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/notify-open-documents/${id}', function (messageOutput) {
console.log('Receiving message: ' + JSON.parse(messageOutput.body));
});
sendName();
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
console.log("Disconnected");
}
function sendName() {
console.log("Sending message : " + JSON.stringify({'documentId' : "${id}" }));
stompClient.send('/ws/lock-document', {}, JSON.stringify({'documentId': "${id}"}));
}
</script>

REQ/REP & DEALER/ROUTER for two-way asynchronous worker processing

I'm learning ZeroMQ and just went through the tutorial and a few examples. I'm using Node.js as my main environment ( with Python eventually used to replace my workers ).
Trying to sort out how I can create a fully asynchronous messaging system that will allow my API to push tasks ( via a REQ socket ) to a router, have a dealer pass the message to a worker, process the message and send its results back up to my client ( which is an Express route ).
I believe the pattern for this would work something like this ( haven't tested or properly implemented code yet, so please take it as a conceptual outline ):
router.js
const zmq = require('zmq');;
const frontend = zmq.socket('router');
const backend = zmq.socket('dealer');
frontend.on('message', function() {
var args = Array.apply(null, arguments);
backend.send(args);
});
backend.on('message', function() {
var args = Array.apply(null, arguments);
frontend.send(args);
});
frontend.bindSync('tcp://*:5559');
backend.bindSync('tcp://*:5560');
client.js
var zmq = require('zmq'),
var express = require('express');
var app = express();
app.post('send', function(req, res) {
var client = zmq.socket('req');
// listen for responses from the server
client.on('message', function(data) {
console.log(data);
client.close();
});
// connect to the server port
client.connect('tcp://0.0.0.0:5454');
client.send('Request from ' + process.id);
});
app.listen('80');
worker.js
var zmq = require('zmq');
var server = zmq.socket('rep');
server.on('message', function(d){
server.send('Response from ' + process.id);
});
// bind to port 5454
server.bind('tcp://0.0.0.0:5454', function(err){
if (err){
console.error("something bad happened");
console.error( err.msg );
console.error( err.stack );
process.exit(0);
}
});
What I'm not fully understanding is if the ROUTER/DEALER will handle sending the response worker to the correct client. Also in this case the Dealer handles the Fair Queueing as I want my work distributed amongst the workers evenly.
My client could be distributed amongst many different boxes ( load balancer API server ), my router will be on its own server and the workers would be distributed amongst multiple boxes as well.
Forget REQ/REP in any production-grade app, can fall in mutual deadlock
You might find this subject in many other posts on high-risk mutual FSM-FSM deadlocking in REQ/REP Formal Scalable Communication Pattern.
Be sure, XREQ/XREP == DEALER/ROUTER ( already since 2011 )
source code removes all hidden magics behind this, XREQ == DEALER and XREP == ROUTER
+++b/include/zmq.h
...
-#define ZMQ_XREQ 5
-#define ZMQ_XREP 6
+#define ZMQ_DEALER 5
+#define ZMQ_ROUTER 6
...
+#define ZMQ_XREQ ZMQ_DEALER /* Old alias, remove in 3.x */
+#define ZMQ_XREP ZMQ_ROUTER /* Old alias, remove in 3.x */
For anyone reading this in the future, in my further research I stumbled on the Majordomo Protocol/pattern. It's precisely what I'm trying to implement. Documentation on the implementation, benefits and disadvantages can be read here: https://rfc.zeromq.org/spec:18/MDP/. Here's the broker implementation: https://github.com/zeromq/majordomo
Seems like I was using DEALER/ROUTER when I should have been using XREQ and XREP.
broker.js
var zmq = require('zmq');
var frontPort = 'tcp://127.0.0.1:5559';
var backPort = 'tcp://127.0.0.1:5560';
var frontSocket = zmq.socket('xrep');
var backSocket = zmq.socket('xreq');
frontSocket.identity = 'xrep_' + process.pid;
backSocket.identity = 'xreq_' + process.pid;
frontSocket.bind(frontPort, function (err) {
console.log('bound', frontPort);
});
frontSocket.on('message', function() {
//pass to back
console.log('router: sending to server', arguments[0].toString(), arguments[2].toString());
backSocket.send(Array.prototype.slice.call(arguments));
});
backSocket.bind(backPort, function (err) {
console.log('bound', backPort);
});
backSocket.on('message', function() {
//pass to front
console.log('dealer: sending to client', arguments[0].toString(), arguments[2].toString());
frontSocket.send(Array.prototype.slice.call(arguments));
});
console.log('Broker started...');
worker.js
var zmq = require('zmq');
var socket = zmq.socket('rep');
socket.identity = 'worker_' + process.pid;
socket.on('message', function(data) {
console.log(socket.identity + ': received ' + data.toString());
socket.send(data * 2);
});
socket.connect('tcp://127.0.0.1:5560', function(err) {
if (err) throw err;
console.log('server connected!');
});
console.log('Worker started...');
client.js
var zmq = require('zmq');
var socket = zmq.socket('req');
socket.identity = 'client_' + process.pid;
socket.on('message', function(data) {
console.log(socket.identity + ': answer data ' + data);
});
socket.connect('tcp://127.0.0.1:5559');
setInterval(function() {
var value = Math.floor(Math.random()*100);
console.log(socket.identity + ': asking ' + value);
socket.send(value);
}, 100);
console.log('Client started...');
I'm still not sure if it's safe opening a connection on EVERY API inbound request.

Call SignalR Server Method in javascript function without event

I have a SignalR Server Method as mentioned below. I was able to call the method inside
public void AddNotification(string message, string toUser)
{
lock (connectedUsers)
{
if (connectedUsers.ContainsKey(toUser))
{
string receiverID = connectedUsers[toUser];
hubContext.Clients.Client(receiverID).addMessage(message);
}
}
}
I was able to call the method from javascript using JS Client.
$(function () {
$("#sendControls").hide("fast");
$.connection.hub.url = 'http://localhost:45210/signalr/hubs';
// Proxy created on the fly
selfHostHub = $.connection.selfHostHub;
// Declare a function on the hub so the server can invoke it
selfHostHub.client.addMessage = function (message) {
$('#messages').append("<li>" + message + "</li>");
};
// Start the connection
$.connection.hub.start().done(function () {
$("#Endcall").click(function () {
selfHostHub.server.addNotification(username + " has disconnected call for loanid " + loanid, supervisorName);
});
});
});
Is it possible to call Server method inside javascript function and instead of OnClick event. Kindly provide with any solution.

Resources