MVC CORE SignalR send messages specific user only - asp.net-core-mvc

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

Related

socket.io buffered messages loss after reconnection

This is on client side
socket.on('connect', () => {
console.log('client connect', socket.id);
const token = getToken();
socket.emit('token', token);
});
socket.on('message', data => {
....
//handle message
});
This is on server side
io.on('connection', (client) => {
client.on('token', token => {
verifyToken(token)
.then(({ _id: clientId }) => {
if (!clientId) return;
if (!connections[clientId]) {
connections[clientId] = new Map();
}
connections[clientId].set(client, 1);
client.on('disconnect', () => {
connections[clientId].delete(client);
});
});
});
});
}
async sendMessageToClients (workspaceId, message) {
const workspace = await getWorkspaceQuery(workspaceId);
if (!workspace) return;
const workspaceMembers = workspace.members.map(({ user }) => user);
for (const memberId of workspaceMembers) {
if (connections[memberId]) {
for (const clientConnection of connections[memberId].keys()) {
console.log('send to client', memberId, message.content, clientConnection.connected, clientConnection.id);
clientConnection.emit('message', message);
}
}
}
};
}
I purposely make a client offline by disconnect the wifi connection (make it in offline mode), what happen is that
a. if the disconnection is short, socket.id stay the same and I can get the buffered message send by other client when comes online;
b. but if I the disconnection is longer, the socket.id will change, and I can't get the buffered message send by other client when comes online.
How should I address that?
Since according to here the messages should be ideally buffered after reconnection.

Change channel dynamic with laravel,Socket and Redis

!!!
Server - socket How can change channal dynamic
When I broadcast using /fire/1 for example, I only want to send to /room/1.
Currently it sends to /room/1, /room/2, /room/3, etc. Because by default here, everything on the server is subscribed to 'test-channel'. I just can't figure this out.
var server = require('http').Server();
var io = require('socket.io')(server);
var Redis = require('ioredis');
var redis = new Redis();
io.on('connection', function(socket){
console.log('New User Conected here');
redis.subscribe('test-channel');
redis.on('message', function(subscribed ,channel, message) {
console.log(channel);
message = JSON.parse(message);
socket.emit(channel + ':' + message.event, message.data);
});
socket.on('joinRoom', function(room ){
console.log('Join in this Room '+ room);
socket.join(room);
});
});
server.listen(3000);
event | php
public function broadcastOn()
{
return ['test-channel']; // static
}
Add a property to your broadcast event, then pass it to the constructor.
class Message implements ShouldBroadcast{
use SerializesModels;
protected $channel;
public function __construct($channel){
$this->channel = $channel;
}
public function broadcastOn(){
return [$this->channel];
}
}
Then when you fire the event, pass the channel in: event(new Message($channel));

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.

How to register Window phone with notification hub in app-backend

Hello friends i am trying to implement the app-backend registration of app with notification hub.for implementing it i am following this notify user with notification hub but i wanted to do registration for windows phone so i have tried to do it and write this code in mobile service Api
exports.post = function(request, response) {
// Use "request.service" to access features of your mobile service, e.g.:
// var tables = request.service.tables;
// var push = request.service.push;
var azure = require('azure');
var hub = azure.createNotificationHubService('samplenotificationhub',
'full access key');
var platform = request.body.platform;
var installationId = request.header('X-ZUMO-INSTALLATION-ID');
var registrationComplete = function(error, registration) {
if (!error) {
// Return the registration.
response.send(200, registration);
} else {
response.send(500, 'Registration failed!');
}
}
// Function called to log errors.
var logErrors = function(error) {
if (error) {
console.error(error)
}
}
hub.listRegistrationsByTag(installationId, function(error, existingRegs) {
var firstRegistration = true;
if (existingRegs.length > 0) {
for (var i = 0; i < existingRegs.length; i++) {
if (firstRegistration) {
// Update an existing registration.
if (platform === 'wp') {
existingRegs[i].ChannelUri = request.body.channelUri;
hub.updateRegistration(existingRegs[i], registrationComplete);
} else {
response.send(500, 'Unknown client.');
}
firstRegistration = false;
} else {
// We shouldn't have any extra registrations; delete if we do.
hub.deleteRegistration(existingRegs[i].RegistrationId, logErrors);
}
}
} else {
// Create a new registration.
if (platform === 'wp') {
hub.mpns.createNativeRegistration(request.body.channelUri,
[request.body.CurrentDate], registrationComplete);
}
else {
response.send(500, 'Unknown client.');
}
}
});
};
i am able to get the api call from this code in my app..
private async Task AcquirePushChannel()
{
CurrentChannel = HttpNotificationChannel.Find("mychannel");
string message;
if (CurrentChannel == null)
{
CurrentChannel = new HttpNotificationChannel("mychannel");
CurrentChannel.Open();
CurrentChannel.BindToShellTile();
CurrentChannel.BindToShellToast();
}
var body = new NotificationRequest
{
channelUri = CurrentChannel.ChannelUri.ToString(),
platform = "wp",
CurrentDate = "1",
};
try
{
// Call the custom API POST method with the supplied body.
var result = await App.MobileService
.InvokeApiAsync<NotificationRequest,
RegistrationResult>("registrationapi", body,
System.Net.Http.HttpMethod.Post, null);
// Set the response, which is the ID of the registration.
message = string.Format("Registration ID: {0}", result.RegistrationId);
registrationid = result.RegistrationId;
}
catch (MobileServiceInvalidOperationException ex)
{
message = ex.Message;
}
i have seen an active api call on mobile service dashboard but not able to get response from API..
i have written this code in my table scripts so that i can send push notification to my phone it..also take a look if anything is wrong in it.
function insert(item, user, request) {
var azure = require('azure');
var hub = azure.createNotificationHubService('samplenotificationhub',
'listen signature string');
// Create the payload for a Windows Store app.
var wnsPayload = '<toast><visual><binding template="ToastText02"><text id="1">New item added:</text><text id="2">' + "tanuj" + '</text></binding></visual></toast>';
var Toasttemplate = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<wp:Notification xmlns:wp=\"WPNotification\">" +"<wp:Toast>" +"<wp:Text1>$(" + "1" + ")</wp:Text1>" +"</wp:Toast> " +"</wp:Notification>";
// Execute the request and send notifications.
request.execute({
success: function() {
// Write the default response and send a notification
// to the user on all devices by using the userId tag.
request.respond();
hub.wpns.send("1", Toasttemplate, 'wpns/toast', function(error) {
if (error) {
console.log(error);
}
});
}
});
i know this is lot of code i am putting this because the link is not mentioned for wp so just wanted to make sure i am doing right.
also please let me know first what is INSTALATIONID in var installationId = request.header('X-ZUMO-INSTALLATION-ID'); hope to get some response. any help ,idea or suggestion is appreciated.

WebSockets - ASP.NET 4.5 IIS 8 Final Release

All the blogs online have been in reference to the beta with the old namespaces and use nuget packages. Im trying to get a simple low level websockets example going based on whats in the wild now and its just not working.
The client is never able to establish a connection. The error from the chrome debug console is:
"Port error: Could not establish connection. Receiving end does not exist. "
However, I know the request is being received because I put code in my ashx handler to email me at various points just to confirm the request was coming in and my task was firing etc.
Config - All Final Release Versions:
Windows Server 2012/IIS 8/ ASP.NET 4.5
My sample is based on:
http://www.asp.net/vnext/overview/whitepapers/whats-new#_Toc318097383
The handler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebSockets;
using System.Net.WebSockets;
using System.Threading;
using System.Text;
using System.Threading.Tasks;
namespace myWebSocket
{
/// <summary>
/// Summary description for wsHandler
/// </summary>
public class wsHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// context.Response.ContentType = "text/plain";
// context.Response.Write("Hello World");
context.AcceptWebSocketRequest(MyWebSocketTask);
}
public async Task MyWebSocketTask(WebSocketContext context)
{
WebSocket socket = context.WebSocket;
while (true)
{
System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage();
message.To.Add("email#address.com");
message.Subject = "Handler";
message.From = new System.Net.Mail.MailAddress("michaelo#hostcollective.com");
message.Body = string.Format("Task Launched {0}", socket.State.ToString());
System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient("localhost");
smtp.Send(message);
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
// Asynchronously wait for a message to arrive from a client
WebSocketReceiveResult result =
await socket.ReceiveAsync(buffer, CancellationToken.None);
// If the socket is still open, echo the message back to the client
if (socket.State == WebSocketState.Open)
{
string userMessage = Encoding.UTF8.GetString(buffer.Array, 0,
result.Count);
userMessage = "You sent: " + userMessage + " at " +
DateTime.Now.ToLongTimeString();
buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMessage));
// Asynchronously send a message to the client
await socket.SendAsync(buffer, WebSocketMessageType.Text,
true, CancellationToken.None);
}
else { break; }
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
As for the client, this is the simplest one based on the stuff at websockets.org
<script language="javascript" type="text/javascript">
// var wsUri = "ws://echo.websocket.org/";
var wsUri = "ws://iis8hosting.com/mikey/wshandler.ashx";
var output;
function init() {
output = document.getElementById("output");
testWebSocket();
}
function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };
}
function onOpen(evt) {
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
}
function onClose(evt) {
writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data + '</span>');
websocket.close();
}
function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message) {
writeToScreen("SENT: " + message);
websocket.send(message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
window.addEventListener("load", init, false);
<h2>WebSocket Test</h2><div id="output"></div>
Anybody have any ideas or another simple sample such as this that is confirmed to work with the final release stuff. Thanks for taking the time to read and respond.
I could make your code work with some changes in your code. You may not require to do all of the below changes in your environment.
On Server side put a try catch block around SMTP code Verify context.IsWebSocketConnection is true
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(MyWebSocketTask);
}
else
{
throw new HttpException("This isn't a WebSocket request!");
}
On client side: I changed code like below based on errors i got :).
This code will work in IISExpress 8 as well.
var wsUri = "ws://<%: Request.Url.Host %>:<%: Request.Url.Port %><%:
Response.ApplyAppPathModifier("~/wshandler.ashx")
Added serverData inside form like below.
<form id="form1" runat="server">
<div id="serverData"></div>
</form>
And used it in writeToScreen() function.
function writeToScreen(message) {
var serverData = document.getElementById("serverData");
var newElem = document.createElement("p");
newElem.style.wordWrap = "break-word";
newElem.innerHTML = message;
serverData.insertBefore(newElem, serverData.firstChild);
}
onMessage() function closes the connection from client side. So only one message is sent and received.
Make sure the IE10 browser's document mode is "Standard". I used IE10 for testing your code.

Resources