Await signalr message in client - websocket

Is it possible for a SignalR client send a message to the server and then to await for a seperate message (not a return value) from the server?
The theory;
Client1 send message1 to Server and "waits" for the response.
Server processes some logic
Server sends message2 to Client1 and Client1 executes the waiting code.
Call to the server:
$.connection.myhub.server.myRequest(id).done(()==>{
// myRequest is done;
// the server has received the request but hasn't processed it yet.
// I want put some *async* code here to do something when the server has triggered $.connection.myhub.client.myResponse(id, someParam);
});
Callback to the client:
$.connection.myhub.client.myResponse(originalId, somePassedBackValue);
Can I use Async/Await, or wrap this in a Promise somehow?
If this isn't acheivable in SignalR are there anyother socket libraries that might be used instead?

You can do something, like the following:
Imagine you have a client that joins a group, updates a table and then notifies the client that it has joined.
Client
msgHub.server.joinGroup(id).done(function () {
console.log("Joined Group");
serverCallFinished = true;
})
msgHub.client.displayTable = function (table) {
display(table);
}
Hub
public async Task JoinGroup(string practiceId)
{
try
{
await Groups.Add(Context.ConnectionId, practiceId);
//Add new table
var table = new Table(practiceId)
await UpdateTable("SomeGroup", table);
}
catch (Exception e)
{
throw;
}
}
public async Task UpdateInfo(string groupName, string table)
{
//await some logic
Clients.Group(groupName).updateTable(table);
}
Update info will call the client with message2 in this case a table that it wants to display to the client. When it finishes the it will return from its awaited state by JoinGroup which will return and alert that a new user has joined a group.

Related

Oracle CQN Event Issue

I am using nodejs to listen to my table's data change using oracle CQN.
I do have the grant permission and vice versa connection from my DB server to my production server. The below code is working fine from a DB server and APP Hosting server
function myCallback(message) {
console.log(`myCallback:: Listened new data insertion`);
runFunction();
}
const options = {
callback: myCallback,
sql: `SELECT * FROM ${schemaName}.${tableName} where STATUS=0`,
qos : oracledb.SUBSCR_QOS_ROWIDS,
clientInitiated: true,
};
async function listener() {
//Create Connection Pool
try {
await oracledb.createPool({
user: `${userName}`,
password: `${userPass}`,
connectString: `${connString}`,
externalAuth: false,
events: true,
poolMax: parseInt(`${poolMax}`),
poolMin: parseInt(`${poolMin}`),
// poolTimeout: parseInt(`${poolTimeout}`),
// queueMax: parseInt(`${queueMax}`),
// queueTimeout: parseInt(`${queueTimeout}`),
});
//Create Connection
const connection = await oracledb.getConnection();
await connection.subscribe('mysub', options);
console.log("App is Listening ");
} catch (e) {
console.log("Error on Listening" + e);
}
}
But while changing to a new DB hosting server I don't receive the change notification. I have used the query to check whether the event registration is working or not.
SELECT * from USER_CHANGE_NOTIFICATION_REGS
I do get many regid which means the event registration is working fine.
So why the CallBack function is not working.
Thanks.

AWS Websocket doesnt receive previous message until new message is sent

Most of the time the messages are passed normally, but a couple messages in particular arent recieved until the recieving client sends a message. This happens everytime for specific methods/messages, but not at all for others.
Example: user1 sends a message, user2 then sends a message to receive message from user1.
Related Material
Deleted question: websocket receives previous message only when new message is sent
Github issue: webSocket client does not receive messages before sending...
We ran into this issue and the solution had to do with how we wrote our promises. We initially used the sample code provided by Amazon
https://github.com/aws-samples/simple-websockets-chat-app/blob/master/sendmessage/app.js#L26
const postCalls = connectionData.Items.map(async ({ connectionId }) => {
try {
await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();
} catch (e) {
if (e.statusCode === 410) {
console.log(`Found stale connection, deleting ${connectionId}`);
await ddb.delete({ TableName: TABLE_NAME, Key: { connectionId } }).promise();
} else {
throw e;
}
}
});
And I'm pretty sure having an async function as a map function doesn't work properly or reliably (for whatever reason. maybe this is documented somewhere), so we changed it to a simple for loop and it fixed the issue.
for(const connection of connectionData.Items) {
const connectionId = connection.connectionId;
...same logic goes here
}

.Net Core SignalR - connection timeout - heartbeat timer - connection state change handling

just to be clear up-front, this questions is about .Net Core SignalR, not the previous version.
The new SignalR has an issue with WebSockets behind IIS (I can't get them to work on Chrome/Win7/IIS express). So instead I'm using Server Sent Events (SSE).
However, the problem is that those time out after about 2 minutes, the connection state goes from 2 to 3. Automatic reconnect has been removed (apparently it wasn't working really well anyway in previous versions).
I'd like to implement a heartbeat timer now to stop clients from timing out, a tick every 30 seconds may well do the job.
Update 10 November
I have now managed to implement the server side Heartbeat, essentially taken from Ricardo Peres' https://weblogs.asp.net/ricardoperes/signalr-in-asp-net-core
in startup.cs, add to public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
app.UseSignalR(routes =>
{
routes.MapHub<TheHubClass>("signalr");
});
TimerCallback SignalRHeartBeat = async (x) => {
await serviceProvider.GetService<IHubContext<TheHubClass>>().Clients.All.InvokeAsync("Heartbeat", DateTime.Now); };
var timer = new Timer(SignalRHeartBeat).Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(30));
HubClass
For the HubClass, I have added public async Task HeartBeat(DateTime now) => await Clients.All.InvokeAsync("Heartbeat", now);
Obviously, both the timer, the data being sent (I'm just sending a DateTime) and the client method name can be different.
Update .Net Core 2.1+
See the comment below; the timer callback should no longer be used. I've now implemented an IHostedService (or rather the abstract BackgroundService) to do that:
public class HeartBeat : BackgroundService
{
private readonly IHubContext<SignalRHub> _hubContext;
public HeartBeat(IHubContext<SignalRHub> hubContext)
{
_hubContext = hubContext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.SendAsync("Heartbeat", DateTime.Now, stoppingToken);
await Task.Delay(30000, stoppingToken);
}
}
}
In your startup class, wire it in after services.AddSignalR();:
services.AddHostedService<HeartBeat>();
Client
var connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
Remaining pieces of the initial question
What is left is how to properly reconnect the client, e.g. after IO was suspended (the browser's computer went to sleep, lost connection, changed Wifis or whatever)
I have implemented a client side Heartbeat that is working properly, at least until the connection breaks:
Hub Class: public async Task HeartBeatTock() => await Task.CompletedTask;
Client:
var heartBeatTockTimer;
function sendHeartBeatTock() {
connection.invoke("HeartBeatTock");
}
connection.start().then(args => {
heartBeatTockTimer = setInterval(sendHeartBeatTock, 10000);
});
After the browser suspends IO for example, the invoke method would throw an exception - which cannot be caught by a simple try/catch because it is async.
What I tried to do for my HeartBeatTock was something like (pseudo-code):
function sendHeartBeatTock
try connection.invoke("HeartbeatTock)
catch exception
try connection.stop()
catch exception (and ignore it)
finally
connection = new HubConnection().start()
repeat try connection.invoke("HeartbeatTock")
catch exception
log("restart did not work")
clearInterval(heartBeatTockTimer)
informUserToRefreshBrowser()
Now, this does not work for a few reasons. invoke throws the exception after the code block executes due to being run asynchronous. It looks as though it exposes a .catch() method, but I'm not sure how to implement my thoughts there properly.
The other reason is that starting a new connection would require me to re-implement all server calls like "connection.on("send"...) - which appears silly.
Any hints as to how to properly implement a reconnecting client would be much appreciated.
This is an issue when running SignalR Core behind IIS. IIS will close idle connections after 2 minutes. The long term plan is to add keep alive messages which, as a side effect, will prevent IIS from closing the connection. To work around the problem for now you can:
send periodically a message to the clients
change the idle-timeout setting in IIS as described here
restart the connection on the client side if it gets closed
use a different transport (e.g. long polling since you cannot use webSockets on Win7/Win2008 R2 behind IIS)
I've got a working solution now (tested in Chrome and FF so far). In the hope to either motivate you to come up with something better, or to save you a little while coming up with something like this yourselves, I'm posting my solution here:
The Heartbeat-"Tick" message (the server routinely pinging the clients) is described in the question above.
The client ("Tock" part) now has:
a function to register the connection, so that the callback methods (connection.on()) can be repeated; they'd be lost after just restarting a "new HubConnection" otherwise
a function to register the TockTimer
and a function to actually send Tock pings
The tock method catches errors upon sending, and tries to initiate a new connection. Since the timer keeps running, I'm registering a new connection and then simply sit back and wait for the next invocation.
Putting the client together:
// keeps the connection object
var connection = null;
// stores the ID from SetInterval
var heartBeatTockTimer = 0;
// how often should I "tock" the server
var heartBeatTockTimerSeconds = 10;
// how often should I retry after connection loss?
var maxRetryAttempt = 5;
// the retry should wait less long then the TockTimer, or calls may overlap
var retryWaitSeconds = heartBeatTockTimerSeconds / 2;
// how many retry attempts did we have?
var currentRetryAttempt = 0;
// helper function to wait a few seconds
$.wait = function(miliseconds) {
var defer = $.Deferred();
setTimeout(function() { defer.resolve(); }, miliseconds);
return defer;
};
// first routine start of the connection
registerSignalRConnection();
function registerSignalRConnection() {
++currentRetryAttempt;
if (currentRetryAttempt > maxRetryAttempt) {
console.log("Clearing registerHeartBeatTockTimer");
clearInterval(heartBeatTockTimer);
heartBeatTockTimer = 0;
throw "Retry attempts exceeded.";
}
if (connection !== null) {
console.log("registerSignalRConnection was not null", connection);
connection.stop().catch(err => console.log(err));
}
console.log("Creating new connection");
connection = new signalR.HubConnection("/signalr", { transport: signalR.TransportType.ServerSentEvents });
connection.on("Heartbeat", serverTime => { console.log(serverTime); });
connection.start().then(() => {
console.log("Connection started, starting timer.");
registerHeartBeatTockTimer();
}).catch(exception => {
console.log("Error connecting", exception, connection);
});
}
function registerHeartBeatTockTimer() {
// make sure we're registered only once
if (heartBeatTockTimer !== 0) return;
console.log("Registering registerHeartBeatTockTimer");
if (connection !== null)
heartBeatTockTimer = setInterval(sendHeartBeatTock, heartBeatTockTimerSeconds * 1000);
else
console.log("Connection didn't allow registry");
}
function sendHeartBeatTock() {
console.log("Standard attempt HeartBeatTock");
connection.invoke("HeartBeatTock").then(() => {
console.log("HeartbeatTock worked.") })
.catch(err => {
console.log("HeartbeatTock Standard Error", err);
$.wait(retryWaitSeconds * 1000).then(function() {
console.log("executing attempt #" + currentRetryAttempt.toString());
registerSignalRConnection();
});
console.log("Current retry attempt: ", currentRetryAttempt);
});
}
Client version based on ExternalUse's answer...
import * as signalR from '#aspnet/signalr'
import _ from 'lodash'
var connection = null;
var sendHandlers = [];
var addListener = f => sendHandlers.push(f);
function registerSignalRConnection() {
if (connection !== null) {
connection.stop().catch(err => console.log(err));
}
connection = new signalR.HubConnectionBuilder()
.withUrl('myHub')
.build();
connection.on("Heartbeat", serverTime =>
console.log("Server heartbeat: " + serverTime));
connection.on("Send", data =>
_.each(sendHandlers, value => value(data)));
connection.start()
.catch(exception =>
console.log("Error connecting", exception, connection));
}
registerSignalRConnection();
setInterval(() =>
connection.invoke("HeartBeatTock")
.then(() => console.log("Client heatbeat."))
.catch(err => {
registerSignalRConnection();
}), 10 * 1000);
export { addListener };

Monolog with Bot Framework

We are using the Microsoft Bot Framework for our chat bot. Our Message Controller is standard :
public async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
HttpResponseMessage response;
try
{
if (activity.Type == ActivityTypes.Message)
{
//do some stuff
await Conversation.SendAsync(activity, () => new RootDialog());
}
else
{
HandleSystemMessage(activity);
}
response = this.Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
//do some catching
}
return response;
}
Sometimes, the bot needs to have long monologs. If it takes too long, we receive a 502 Bad Gateway error.
Any solution for that ?
Bot Framework calls time out after 15 seconds. Your bot must return a successful HTTP status code within 15 seconds for Direct Line to consider the message successfully sent.
If your bot does a lot of offline work or sends multiple messages, you should move that work to a background thread so the incoming request completes within 15 seconds.
Here's a snippet that correctly handles loads hosted inside ASP.Net and other hosting environments.
if (HostingEnvironment.IsHosted)
{
HostingEnvironment.QueueBackgroundWorkItem(c => DoWorkAsync());
}
else
{
Task.Run(() => DoWorkAsync());
}
...
private async Task DoWorkAsync()
{
// do some stuff
}
Task.Run is simpler but HostingEnvironment.QueueBackgroundWorkItem prevents ASP.Net from tearing down your process before this work is complete.

myWebSocketSubject.multiplex(..).subscribe().unsubscribe() closes connection, event further observers exists

The following code will close the connection, event further observers exists on the myWebSocketSubject:
myWebSocketSubject.Observable.webSocket('ws://mysocket');
myWebSocketSubject.subscribe();
myWebSocketSubject.multiplex(..).subscribe().unsubscribe()
// the connection closed now
My expectation was, that the connection gets closed with the last unsubscribe() call (and not with the first one).
Use Case
If I get it right, with the multiplex(..) operator, on create and complete a message is send to the socket, which e.g. allows to un-/subscribe on server side to specific event.
My preferred Web Socket service could therefore look like as below. There exists only one connection, and this single connection provides several streams. On first subscription to the web socket the connection gets created; and with the last unsubscribe call the connection gets closed. For each data-stream a un-/subscribe message is sent once.
I haven't found a solution to use the WebSocketSubject.multiplex(..) method...
Preferred Example Web Socket Service
export class WebSocketService {
connection: WebSocketSubject<any>;
constructor() {
this.connection = Observable.webSocket<any>(_createConfig())
}
dataStream(eventType: string): Observable<WebSocketMessage> {
return connection.multiplex(
() => new WebSocketMessage("WebSocket.Subscribe." + eventType),
() => new WebSocketMessage("WebSocket.Unsubscribe." + eventType),
message => (message.type == eventType)
)
.retry() // reconnect on error and send subscription messages again
.share(); // send messages on last/fist un-/subscribe on this stream
}
// ...
}
export class WebSocketMessage {
type: string;
data: any;
constructor(command: string, data?:any) {
this.type = command;
this.data = data || undefined;
}
}
I have written the following test case which fails...
it('should able to handle multiple subscriptions', () => {
const subject = Observable.webSocket(<any>{url: 'ws://mysocket'});
const sub1 = subject.subscribe();
const sub2 = subject.subscribe();
const socket = MockWebSocket.lastSocket;
socket.open();
sinon.spy(socket, 'close');
sub1.unsubscribe();
// Fails, because the socket gets closed on first unsubscribe
expect(socket.close).have.not.been.called;
sub2.unsubscribe();
expect(socket.close).have.been.called;
});
If I get it right the share operator would do the trick. But after using the operator, the multiplex method is not available.
Thanks for any feedback, input, ...!

Resources