Reputation: 1902
I have a web-server that hosts my site. I have a separate server that is running a certain task. As soon as there are results I would like to ping clients currently logged in on my web-server. This should be done using SignalR
This set-up without a service running server is working:
I have a separate project that hosts my hub:
public class NotificationHub : Hub
{
public string Activate() { return ""; }
public override Task OnConnected()
{
string username = Context.User.Identity.Name.ToLower();
Groups.Add(Context.ConnectionId, username);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
return base.OnReconnected();
}
}
and the client side (this code is executed on log in)
var notificationHub = $.connection.notificationHub;
notificationHub.client.connected = function () { console.log('client connected fired!'); };
$.connection.hub.start().done(function () {
notificationHub.server.activate();
})
notificationHub.client.testPing = function (message) {
console.log("Message received: "+message);
};
Then I have a controllet which called BubbleController with action
public ActionResult NotifyPing(string username, string message)
{
SignalRfunctions.TestPing(username,message);
return Content("OK");
}
where SignalRFunction is a static class that contains all communication functions and it is encloded in a separate project called Notifications
public class SignalRfunctions
{
public static void TestPing(string username, string message)
{
IHubContext signalR = GlobalHost.ConnectionManager.GetHubContext<NotificationHub.NotificationHub>();
signalR.Clients.Group(username).TestPing(message);
}
}
Now, when I compile everything log in as Bob and execute this command in browsers console
$.get('/bubble/notifyping?username=bob&message=hi',function(){});
I get a response in the console:
Message received: Hi
Which is what is expected (look at the client side signalR support)
Now what's not working:
When I call
SignalRfunctions.TestPing("bob"," hello from jobrunner");
from within my service (non-web related project), I do not get any response in my browser's console even though I am logged in as bob. Basically, SignalR is working for me in my web-appliactioin, when I try to bridge with my server running service I get no connection.
What am I doing wrong ?
(I did run everything under debug to check if there are any obvious errors, but none were found)
Update: in the initial question I missed a piece of code that initializes SignalR here it is:
[assembly: OwinStartup(typeof(WebApp.OwinStartup))]
namespace WebApp
{
public class OwinStartup
{
public void Configuration(IAppBuilder app)
{
UsernameProvider usernameProvider = new UsernameProvider();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => usernameProvider);
app.MapSignalR();
}
}
}
where UsernameProvider class is here:
public class UsernameProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
string username = request.User.Identity.Name.ToLower();
return username;
}
}
Upvotes: 0
Views: 1823
Reputation: 1196
You have most of the server logic displayed except for the start up routine. Its where you hook up the path for clients to listen to. I also don't see where you are telling where the client should look when connecting to the server, but since it's going back to the server I'm guessing you pointed it there successfully.
http://www.asp.net/aspnet/overview/owin-and-katana/owin-startup-class-detection
Or even here http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host
[assembly: OwinStartup(typeof(Startup))]
namespace YourSignalR
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
// Debugging only
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
// custom path for clients to listen to. By default it is /signalr
app.MapSignalR(Resources.Url, hubConfiguration);
}
}
}
Update - I've been really busy.
From what you are saying you have a Web-Server as the SignalR server and a web-client communicating to it. You want to have a separate SOA based solution that either has services on it or calls other services. You want the services solution to reach out to the SignalR Web-Server.
From what you're saying I don't believe that the HubContext which you are contacting from the services solution is the same Context in which the web-client has connected to so it'll have no one to reach out to when you tell it to send a message. you can debug and check the identity of both I believe.
There are 2 ways that I'm familiar with that could help you.
If you want you can do a self-hosted SingalR server which you would implement with your services back end. I've used this to have a WPF client receive updates from service calls that are made to internal services and external webservices.
Another way is you could treat your services project as another client and have it reach out to the web-server via SignalR to communicate to the other clients/groups. This way they are all in synch and are on the same context. So anything that the server wants to send it'll just invoke a method from the web-server to tell the other clients/groups.
If I think of something else I'll post it.
I made a quick example of this if you want to go with this method, I used Clients.All rather than setting up groups to make it simpler and show that it works.
Web Portion Hub
namespace SignalRChatter
{
/// <summary>
/// install-package Microsoft.AspNet.SignalR
/// </summary>
public class ChatHub : Hub
{
public void Send(string name, string message)
{
Clients.All.broadcastMessage(name, message);
}
}
}
Start Up
[assembly: OwinStartup(typeof(SignalRChatter.Startup))]
namespace SignalRChatter
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application,
// visit http://go.microsoft.com/fwlink/?LinkID=316888
app.MapSignalR();
}
}
}
HTML Body
<body>
<div class="container">
<input type="text" id="message" />
<input type="button" id="sendmessage" value="Send" />
<input type="hidden" id="displayname" />
<ul id="discussion"></ul>
</div>
<!--Script references. -->
<!--Reference the jQuery library. you are using -->
<script src="Scripts/jquery-2.1.1.min.js"></script>
<!--Reference the SignalR library. you are using-->
<script src="Scripts/jquery.signalR-2.1.2.min.js"></script>
<!--Reference the autogenerated SignalR hub script. you are using -->
<script src="signalr/hubs"></script>
<!--Add script to update the page and send messages.-->
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (name, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
</script>
</body>
Server Side Behaving Like a Client Anything that you entered in the console would be sent to the webclient. it's possible you would invoke a method specifically for the back end clients to hit to talk to web clients. Up to you.
Make sure to install this package : This package supports WinRT, Silverlight, WPF, console application, and Windows Phone clients, for both .NET 4 and .NET 4.5. PM> Install-Package Microsoft.AspNet.SignalR.Client
namespace SomeConsoleApp
{
class Program
{
//PM> Install-Package Microsoft.AspNet.SignalR.Client
static void Main(string[] args)
{
var hubConnection = new HubConnection("http://localhost:YOURPORT/");
IHubProxy chatHubProxy = hubConnection.CreateHubProxy("ChatHub");
// it doesn't need to listen to the other clients but was testing to make sure it can. all it needs to do is send to the others.
chatHubProxy.On<string, string>("broadcastMessage", (name, message) => Console.WriteLine("update for {0} new message {1}", name, message));
Connect(hubConnection);
var input = "Server Acting Like A Client";
Console.WriteLine(input);
while (!input.Equals("Exit", StringComparison.InvariantCultureIgnoreCase))
{
input = Console.ReadLine();
Invoker(chatHubProxy, input);
}
Console.ReadLine();
}
static async void Connect(HubConnection hubConnection)
{
try
{
Console.WriteLine("starting");
await hubConnection.Start();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static async void Invoker(IHubProxy chatHubProxy, string input)
{
await chatHubProxy.Invoke("Send", "Server Client", input);
}
}
}
Upvotes: 3
Reputation: 4274
If I understood well you have 1 web app, and another app.
You want that when something happens on the second app, send a message via signalR to someone logged in into your website.
If this is the case, you can make a call to your webapp from the app with and httpclient
http://www.dotnetperls.com/httpclient
to your endpoint /bubble/notifyping passing the message you want.
Upvotes: 1