I'm developing a SignalR application in which set of .NET console clients connecting to a web server. How can I call a specific client from the server using SignalR.
In the current application I did, when I click button from the server side. It triggers all the Console clients. But what I want to trigger is keeping all client information on server.html and call specific client.
Currently this is my console application
class Program
{
static void Main(string[] args)
{
var connection = new Connection("http://localhost:65145/printer");
//Establishing the connection
connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("Failed to start: {0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Success! Connected with client connection id {0}", connection.ConnectionId);
}
});
//Reciveing data from the server
connection.Received += data =>
{
Console.WriteLine("Receiving print request from server");
Console.WriteLine(data);
};
Console.ReadLine();
}
}
This is the server side.
server html which calls clients
<script type="text/javascript">
$(function () {
var connection = $.connection('/printer');
connection.received(function (data) {
//$('#messages').append('<li>' + data + '</li>');
});
connection.start().done(function() {
$('#print').click(function () {
var printThis = { value: 113, reportId: 'Report', printer: '3rd Floor Lazer Printer', Copies: 1 };
connection.send(JSON.stringify(printThis));
});
});
});
</script>
Global.asax
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs();
RouteTable.Routes.MapConnection<MyConnection>("printer", "/printer");
}
The documentation shows how to do this https://github.com/SignalR/SignalR/wiki/PersistentConnection#sending-to-a-specific-connection
Related
I am new to signalr,
I have gone through a number of tutorials where I have seen the chat room application where server has been configured on console app and client on javascript, as you open the multiple instances of browsers you can chat between them.
My question is how to chat between server to client rather than between clients.
lets say I build a server on winform. On that winform I have datagridview which shows me the number of clients connected to server and I want to send a message to client #2 , then the client #2 will also reply me which I will show in a textbox on winform.
My Client Code:
<script type="text/javascript">
$(function () {
$.connection.hub.url = "http://localhost:8080/signalr";
var chat = $.connection.myHub;
// Create a function that the hub can call to broadcast messages.
chat.client.addMessage = function (name, message) {
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
$('#displayname').val(prompt('Enter your name:', ''));
$('#message').focus();
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
chat.server.send($('#displayname').val(), $('#message').val());
$('#message').val('').focus();
});
});
});
</script>
Myhub.cs:
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
public void getname(string name)
{
}
public override Task OnConnected()
{
UserHandler.ConnectedIds.Add(Context.ConnectionId);
string RemoteIpAddress = Context.Request.GetRemoteIpAddress();
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
UserHandler.ConnectedIds.Remove(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
public void acknowledgment(string ack)
{
Clients.Caller.acknowledgment(ack);
}
}
Code seems good. try to add following code in startup.cs
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
I am calling a client-side method from the server that is essentially a Javascript confirm dialog. If the client clicks theCancel button on the dialog I want to delete their token from local storage (essentially logging them out) ...
sessionStorage.removeItem('access-token');
If the client clicks the OK button I want to do the same on a different client (I have the connectionId of this client stored on the server side). So I need a way for the server to know which option the client chose (OK or Cancel).
How would I go about taking the client's choice and passing it back to the server, so the server could call the "Logout" Javascript on the proper client?
I think you have a couple of options.
You could send a message back to the Hub once the user has clicked on a button.
Hub
[HubName("message")]
public class MessageHub : Hub
{
public void sendmessage(bool logout)
{
Clients.Caller(????).logoutuser(logout); //However you identify who to logout??
}
}
Client
var hub = $.connection.message;
hub.client.logoutuser = function(message) {
if (message.logout = true) {
sessionStorage.removeItem('access-token');
}
}
hub.server.sendmessage(true); //result of the user's click OK -> True, Cancel -> False
$.connection.hub.start().done(function () { });
Or you could hit an API that gets the connection for some? user who you're wanting to log out.
API
[RoutePrefix("api/messaging")]
public class MessagingController : ApiController
{
[Route("")]
public void Post(Message message)
{
var notificationHub = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
if (notificationHub != null)
{
try
{
notificationHub.Clients.User(message.UserName).logoutuser(message);
}
catch (Exception ex)
{
}
}
}
}
Client
function notifyController(responseObj) {
$.ajax({
url: '/api/Messaging/',
type: 'POST',
data: responseObj, // some object containing the users response information?
success: function (data) { return; },
error: function (ex) { return; }
});
}
I want to send some data from server to all connected clients using hubs after a specific interval. How can I accomplish this using signalr hubs.
Spin up the System.Threading.Timer, and from it's callback broadcast the message using specific hub.
Global.asax:
private Timer timer;
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs("~/signalr2");
timer = new Timer(TimerCallback(timerCallback), null, Timeout.Infinite, 1000);
}
}
Check the “Broadcasting over a Hub from outside of a Hub” section in SignalR wiki page.
Use ReactiveExtensions and then setup an Observable.Interval call. Then reactive will automatically call the lambda which can broadcast to your clients.
I have stumbled upon this post by Jason Roberts => http://dontcodetired.com/blog/post/Using-Server-Side-Timers-and-SignalR-in-ASPNET-MVC-Applications.aspx
He uses IRegisteredObject and HostingEnvironment.RegisterObject then a System.Threading.Timer in the class that does the work, I haven't tried it myself, but it looks exactly the sort of thing.
Just add
Thread.Sleep(5000);
in your send Method.
Ex:
public void Send(string name, string message)
{
Thread.Sleep(5000);
//call the broadcast message to upadate the clients.
Clients.All.broadcastMessage(name, message);
}
Hope it helps.
Edit
The following code renders the current time for every 5 seconds.
Here is script for it:
<script type="text/javascript">
$(function () {
$.connection.hub.logging = true;
$.connection.hub.start();
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
//Appending the responce from the server to the discussion id
chat.client.currentTime = function (time) {
$('#discussion').append("<br/>" + time + "<br/>");
};
// Start the connection.
$.connection.hub.start().done(function () {
//Call the server side method for every 5 seconds
setInterval(function () {
var date = new Date();
chat.client.currentTime(date.toString());
}, 5000);
});
});
</script>
<div id="discussion"></div>
And on the HubClass write the following:
public class ChatHub: Hub
{
public void currentTime(string date)
{
Clients.All.broadCastTime(date);
}
}
So I was using SignalR version .5 and everything was working fine. But trying to upgrade to version 1 to use the connectionSlow method. Unfortunately it seems to have broken when I have upgraded. I have an mvc application, and I am trying to use signalr to push data to the client. I want the connection to be open forever.
The server will not send messages to the client. After some investigations using a LoggingPiplineModule i found that the context.Connection.Identifier is not the connextionID of the connected browser, its asif it is trying to send it to someone else.
My Hub only has a few methods:
public void JoinGroup(string groupID)
{
if (!String.IsNullOrEmpty(Context.User.Identity.Name) && (!String.IsNullOrEmpty(groupID)))
{
Groups.Add(Context.ConnectionId, groupID.Trim());
}
else
{
LoggerSingleton.Instance.Logger.Error("Error: Could not join group as user is not logged in or group is null");
}
}
public void LeaveGroup(string groupID)
{
if (!String.IsNullOrEmpty(Context.User.Identity.Name) && (!String.IsNullOrEmpty(groupID)))
{
Groups.Remove(Context.ConnectionId, groupID.Trim());
}
else
{
LoggerSingleton.Instance.Logger.Error("Error: Could not leave group as user is not logged in or group is null");
}
}
public static void SendCallLog(CallLog newCall, int groupID)
{
var context = GlobalHost.ConnectionManager.GetHubContext<CommandCentreHub>();
context.Clients.Group(groupID.ToString()).addMessage(CallLog.ToJson(newCall), groupID.ToString());
}
And my javascript:
conChat = $.connection.commandcentrehub;
// Push method for signalR, process the pushed message passed from the server
conChat.addMessage = function (message, groupID) {
var call = JSON.parse(message);
updateTableImages($('#groupContent' + groupID), call, groupID);
updateTableImages($('#groupContent' + 'All'), call, 'All');
applyFilter();
};
$.connection.hub.start().done(function () {
$('.groupID').each(function () {
conChat.server.joinGroup(this.id.replace("group", ""));
});
});
And my global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteTable.Routes.MapHubs();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule());
log4net.Config.XmlConfigurator.Configure();
}
I get no errors in chrome dev, and joingroup is working properly but when the server calls addMessage I get nothing.
Ok I fixed the issue.
It was with my javascript.
The below:
conChat.addMessage = function (message, groupID) { ...
should be:
conChat.client.addMessage = function (message, groupID) {
Hope this helps someone...
I created a server hub in an asp.net application like below
public class Calc : Hub
{
public void CalculateSomething(int a, int b)
{
// start working in a new thread
var task = Task.Factory.StartNew(() => DoCalculate(a, b));
// attach a continuation task to notify
// the client when the work is done
task.ContinueWith(t =>
{
System.Threading.Thread.Sleep(2000);
Clients.addMessage(t.Result);
Caller.notifyCalculateResult(t.Result);
System.Threading.Thread.Sleep(2000);
Caller.notifyCalculateResult("Completed");
Clients.addMessage("Completed");
});
}
private int DoCalculate(int p1, int p2)
{
// do some slow work on the input,
// e.g. call webservice or I/O.
int result = p1 + p2;
//int result = DoSlowWork(p1, p2);
return result;
}
}
Now in another asp.net application I created a client using SiganlR client. But it's not working correctly. I am looking to get data from server as it pushes to client
using System.Threading.Tasks;
using SignalR;
using SignalR.Client;
using SignalR.Client.Hubs;
namespace WebApplication2
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Connect to the service
var hubConnection = new HubConnection("http://localhost:3119/");
// Create a proxy to the chat service
var chat = hubConnection.CreateProxy("Calc");
// Print the message when it comes in
chat.On("addMessage", message =>Print(message));
// Start the connection
hubConnection.Start().Wait();
// Send a message to the server
chat.Invoke("CalculateSomething", 1, 2).Wait();
}
private async void Print(object message)
{
Response.Write(message);
}
}
}
The console client application works fine. The main problem is with asp.net beacause it fails to the handle call back from server.
Looks like you calling the server side method wrongly, try this
chat.Invoke("CalculateSomething", 1, 2).ContinueWith(task =>
{
Console.WriteLine("Value from server {0}", task.Result);
});