SignalR hub hangs on IIS - asp.net

We use SignalR library in our ASP.NET web application. The code looks as following:
Server:
[HubName("ticketsCounterHub")]
public class MassivePrintHub : Hub
{
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
}
public class HubFactory
{
private HubFactory() {}
public static readonly HubFactory Current = new HubFactory();
public IHubProxy GetMassivePrintHubProxy()
{
var hubConnection = new HubConnection(ConfigUtils.GetRequiredSettingValue("adminPath"));
var hubProxy = hubConnection.CreateHubProxy("ticketsCounterHub");
hubConnection.Start().Wait();
return hubProxy;
}
}
Client (JavaScript):
MassivePrintApp.controller("ListController", function ($scope, Dates) {
var hubManager = (function () {
var massivePrintHub = $.connection.ticketsCounterHub;
$.connection.hub.start();
return { massivePrintHub: massivePrintHub };
} ());
hubManager.massivePrintHub.client.Send = function (ticketsCount) {
$scope.action.Quantity = ticketsCount;
$scope.$digest();
};
});
The key part of code is in MVC controller:
public FileResult PrintAction(int actionId, int count, DateTime actionDate, bool isThermo=false)
{
var ticketsCount = _ticketService.GetTicketsInStatusCount(actionId, actionDate, TicketStatusEnum.ToPrint);
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
var stream = new MemoryStream();
xmlResponse.Save(stream);
stream.Flush();
stream.Position = 0;
return File(stream,ContentTypeEnum.XML.ToString(),String.Format("массовая {0} мероприятия {1} {2}шт.xml", isThermo?"термопечать":"печать", action.Artist,count));
}
As you can see, we have this line:
HubFactory.Current.GetMassivePrintHubProxy().Invoke("PostTicketsCount", ticketsCount);
And that causes the issue, that is whenever we call it one more instance of hub was added to "Requests" section on IIS.
I understand we already started hub in JavaScript code, but I'm not sure how can I use the existing connection or how to get rid of HubFactory or delete created hub instance.
And I don't understand why hub hangs on IIS.

Starting from a more simple example will help you a lot I guess. After that you can look into hosting your SignalR server differently (Console App or Windows Service) the basics won't change
(First installed SignalR: NuGet: install-package Microsoft.AspNet.SignalR)
I made a simple web-app example. The project has a Hub class:
using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace SRTest
{
public class MassivePrintHub : Hub
{
private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MassivePrintHub>();
// Can be called from your Javascript code
public void PostTicketsCount(long count)
{
Clients.All.Send(count);
}
// Can be called from your c# code
public static void Static_PostTicketsCount(long count)
{
hubContext.Clients.All.Send(count);
}
}
}
An Owin startup class:
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SRTest.Startup))]
namespace SRTest
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
app.MapSignalR(hubConfiguration);
}
}
}
Page (Razor just to be able to call a simulator which calls a c# class to post message from backend):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>TEST PAGE</title>
<!--Reference the jQuery library. -->
<script src='Scripts/jquery-1.6.4.js'></script>
<!--Reference the SignalR library. -->
<script src='Scripts/jquery.signalR-2.2.0.js'></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
</head>
<body>
THIS IS A TEST PAGE
<!-- Call simulator (trigger event every 5 seconds) -->
#{SRTest.SendFromBackEnd.SimulateSend();}
<script>
$(function () {
var printHub = $.connection.massivePrintHub;
// when send event happens
printHub.client.send = function (count) {
console.log("Send " + count + " tickets");
};
$.connection.hub.start().done(function () {
console.log("Connected");
});
$.connection.hub.logging = true;
});
</script>
</body>
</html>
And I added a dummy class which triggers the event through hubcontext every 5 seconds.
using System.Threading;
using System.Web;
namespace SRTest
{
public class SendFromBackEnd
{
public static void SimulateSend()
{
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
while (true)
{
MassivePrintHub.Static_PostTicketsCount(2);
Thread.Sleep(5000);
}
}).Start();
}
}
}
I added some loggings to the SignalR, add some debug points, it will help you understand the basics, then it will be much easier to build what you are planning to build.
EDIT
About the hanging request: As long as you have a client connected to your SignalR server with SSE or AJAX Long-Polling, you will have an ongoing request, which never finishes. (In case of AJAX Long-polling, it finishes for very short times and comes back). In the apps where I use only Javascript clients, I only see the request if a page is open where I am listening to events. If no page or static page open then no request.
In the apps where I am using .NET clients, as long as the two apps are running, and both Sartup classes executed, the request will always be there, even if no page open. (Since the .NET client is still listening to events.)
For more info: http://hanselminutes.com/291/damian-edwards-explains-the-realtime-web-for-aspnet-with-signalr

This is a Threading related issue. Try like this
Task.Run(() => connection.Start().ContinueWith(task =>
{
.....
})).Wait();

Related

Calling SignalR Hub method from code behind

I had a web Application with master page and I need to show a alert to all connected users of my application. I had used the Owin startup class and mapped signal R
Then created a Hub like below
namespace ArtWebApp
{
[HubName("artHub")]
public class ArtWebHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
public void SayMessage()
{
this.Clients.All.showmessage();
}
}
}
Then in the masterpage I added the Javascript as below
<script src="Scripts/jquery-1.6.4.js"></script>
<script src="Scripts/jquery.signalR-2.2.1.js"></script>
<script type="text/javascript">
$(function () {
debugger;
var connection = $.hubConnection("")
var hub = connection.createHubProxy('artHub');
hub.on('showmessage', function () {
alert('Hi');
});
connection.start().done();
//connection.start(function () {
// hub.invoke('SayMessage');
//});
});
</script>
This is working perfectly when the Hub method is invoked from the same page but when I tried to call the method from button click of a page its not working
protected void Button1_Click(object sender, EventArgs e)
{ var hubContext = GlobalHost.ConnectionManager.GetHubContext<ArtWebApp.ArtWebHub>();
hubContext.Clients.All.SayMessage();
}
Can somebody suggest me the issue
What i can see from your code is a mistake on the client side function you are calling.
Clients.All typically lets you invoke a function you have defined at the client side by calling Clients.All.functionName()
In the Button1_Click event please change
hubContext.Clients.All.SayMessage();
To
hubContext.Clients.All.showMessage();
This is because you are using the dynamic collection Clients
You are trying to invoke a client side function (which doesn't exist).
The method SayMessage you are trying to call is a member of the ArtWebHub class and cannot be invoked by calling hubContext.Clients.All.
You can invoke SayMessage from the client using hub.invoke('SayMessage') but to invoke the showmessage function defined in the client you'll have to invoke it differently from the server because SayMessage is not available to the hubContext

Self hosted SignalR method not called

I am new to SignalR and having a bit of difficulty using a SignalR hub in a self hosted scenario.
At the moment (just for testing) I have the simplest hub possible:
public class NotificationsHub : Hub
{
public void Hello(string name)
{
Clients.All.hello("Hello " + name);
}
}
This hub class is in a Class Library project which is referenced in a Windows Service application. I've added all the nuget packages to the Windows Service app and added the OWIN Startup class which looks like this:
public class Startup
{
public void Configuration(IAppBuilder app)
{
AppDomain.CurrentDomain.Load(typeof(NotificationsHub).Assembly.FullName); // as selfhosting doesn't scan referenced libraries for Hubs
app.Map("/signalr", map => {
map.UseCors(CorsOptions.AllowAll);
var hubConfig = new HubConfiguration {
EnableDetailedErrors = true,
EnableJSONP = true
};
map.RunSignalR(hubConfig);
});
}
}
In the Windows Service OnStart method I host the signalR using:
SignalR = WebApp.Start<Startup>("http://*:9191/");
In the ASP.NET MVC app which should interact with SignalR I have the following code:
<script src="Scripts/jquery.signalR-2.1.1.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="http://localhost:9191/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
$.connection.hub.url = "http://localhost:9191/signalr";
var notif = $.connection.notificationsHub;
notif.client.hello = function (msg)
{
alert(msg);
}
$.connection.hub.start().done(function () {
$("#sayHello").click(function () {
notif.server.hello($("#myName").val());
});
});
});
</script>
The problem is this doesn't work in my correct setup... in the browser console I have no errors, the ~/signalr/hubs js looks fine...
If I do more or less the same configuration, but host the SignalR in the ASP.NET MVC app, everything works as expected.
UPDATE: following #halter73 's suggestion to enable the client side logging for the hub, I've got the following error message which I still can't fix:
SignalR: notificationshub.Hello failed to execute. Error: Method not
found: 'Microsoft.AspNet.SignalR.Hubs.IHubCallerConnectionContext
Microsoft.AspNet.SignalR.Hub.get_Clients()'.
Could somebody please let me know what I am missing?
Thank you in advance!
Andrei
Since 2.1.0, get_Clients should return a IHubCallerConnectionContext<dynamic> instead of IHubCallerConnectionContext.
The error you are seeing could happen if you compile your application against SignalR <= 2.03 but loading SignalR >= 2.10 at runtime.

SignalR with MVC4, cannot push from action on controller

I have a MVC4 application setup to test SignalR push notification and which is not working. (I have tested the chat application on the MVC official site and its working perfectly, the problem is when i pushing a message from controller). Please someone help me on this.
(1) I have startup cs as follows,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Owin;
using Owin;
namespace MvcApplication3
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
(2) Hub class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
namespace MvcApplication3
{
[HubName("notificationHub")]
public class NotificationHub : Hub
{
}
}
(3) what im doing on controller is,
public ActionResult Index()
{
SendMessage("This should be displayed on client !!");
return View();
}
private void SendMessage(string message)
{
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
context.Clients.All.viewMessage(message);
}
(4) Finally on the Index view,
<style type="text/css">
#span-display
{
color: red;
font-size: 16px;
}
</style>
<span id="span-display"></span>
#section scripts
{
<script src="~/Scripts/jquery.signalR-2.0.1.min.js"></script>
<script src="~/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var notificationhub = $.connection.notificationHub;
notificationhub.client.viewMessage = function (message) {
$('#span-display').html(message);
};
$.connection.hub.start();
});
</script>
}
please do help me guys , im on a deadline and couldn't solve this out. Please excuse me if this is a simple thing but im new to this technology.
Thank you in advance :-)
I tested with a code almost equal, and it works, the only diference I found was in the startup class, there I have as well:
[assembly: OwinStartup(typeof(SignalR_Example.Hubs.Startup))]
namespace SignalR_Example.Hubs
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
}
Try using the first line changing my values with your values, other thing important is the JS, have you checked your browser console?, is there any error or something like that?

Self host SignalR with Cross domain ASP.Net Client callback fail

I have a WPF application which use SignalR to achieve publish/subscribe model.
When I used a WPF client to connect to the above application, the publish and callback worked successfully.
Then I created a ASP.Net client. I use a cross domain property of SignalR to connect to above WPF application.
It could connect to the application and call the method provided in the hub successfully.
However, when the WPF application call the method in the ASP.Net Client, it seems that that call cannot be reached to the client browser
(viewed in Firefox, the long polling does not return; break point cannot be reached even I have set the break point in the javascript callback function, and nothing could be displayed in the broswer).
I have included the following script in html
<script src="#Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript</script>
<script src="/Scripts/jquery.signalR-1.0.0-rc2.min.js" type="text/javascript"></script>
<script src="http://localhost:9999/signalr/hubs" type="text/javascript"></script>
The following is the javascript that I have used.
jQuery.support.cors = true;
myHub = $.connection.subscriberHub;
myHub.client.addMessage = function (msg, time) {
$("#message").prepend("<div>" + time + " " + msg + "</div>");
};
$.connection.hub.url = 'http://localhost:9999/signalr';
$.connection.hub.start();
The below is the server code in the WPF application:
public partial class App : Application
{
private IDisposable app;
private void Application_Startup(object sender, StartupEventArgs e)
{
string url = "http://localhost:9999";
app = WebApplication.Start<Startup>(url);
}
private void Application_Exit(object sender, ExitEventArgs e)
{
if (app != null)
{
app.Dispose();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapHubs();
}
}
And I send the message when the WPF application clicked a button:
private void btn_sendMsg_Click(object sender, RoutedEventArgs e)
{
var context = GlobalHost.ConnectionManager.GetHubContext<SubscriberHub>();
DateTime sentTime = DateTime.Now;
context.Clients.Group("subscriber").addMessage(tb_message.Text, sentTime);
MessageList.Insert(0,string.Format("{0:yyyy-MM-dd HH:mm:ss} {1}", sentTime, tb_message.Text));
}
The following is the hub that I have defined:
public class SubscriberHub : Hub
{
string group = "subscriber";
public Task Subscribe()
{
return Groups.Add(Context.ConnectionId, group);
}
public Task Unsubscribe()
{
return Groups.Remove(Context.ConnectionId, group);
}
}
Is there any problem in the above code?
Don't you need to have the JavaScript client call Subscribe?
$.connection.hub.start().done(function () {
myHub.server.subscribe();
});
Alternatively you could modify SubscriberHub:
public class SubscriberHub : Hub
{
string group = "subscriber";
public override Task OnConnected()
{
return Groups.Add(Context.ConnectionId, group);
}
// ...
}

SignalR as WCF web socket service

Is it possible to host SignalR as a part of WCF websocket service and not as a part of ASP.net web site. I am aware about pushing mesage from a web service to signalR clients but is it also possible tht when the socket connection is opened from browser it maps to a web serivce contract?
You can self-host the SignalR server:
Taken from (https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs):
Getting Started
To get started, Install the following packages:
Install-Package Microsoft.Owin.Hosting -pre
Install-Package Microsoft.Owin.Host.HttpListener -pre
Install-Package Microsoft.AspNet.SignalR.Owin -pre
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
namespace SignalR.Hosting.Self.Samples
{
class Program
{
static void Main(string[] args)
{
string url = "http://172.0.0.01:8080";
using (WebApplication.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
// This will map out to http://localhost:8080/signalr by default
// This means a difference in the client connection.
app.MapHubs();
}
}
public class MyHub : Hub
{
public void Send(string message)
{
Clients.All.addMessage(message);
}
}
}
You can host the SignarR hub in any .Net application, like:
public class Program
{
public static void Main(string[] args)
{
// Connect to the service
var hubConnection = new HubConnection("http://localhost/mysite");
// Create a proxy to the chat service
var chat = hubConnection.CreateProxy("chat");
// Print the message when it comes in
chat.On("addMessage", message => Console.WriteLine(message));
// Start the connection
hubConnection.Start().Wait();
string line = null;
while((line = Console.ReadLine()) != null)
{
// Send a message to the server
chat.Invoke("Send", line).Wait();
}
}
}
Ref: https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs
If there any specific reason you want to use WCF? you can write your service as SignarR hub only.

Resources