Scenario
I'm attempting to use linqpad to self host SignalR.
Script
// I have uploaded to instant share here: http://share.linqpad.net/578ol2.linq
// Import the following nuget packages:
// Microsoft.AspNet.SignalR.SelfHost
// Microsoft.Owin.Cors
#define NONEST
void Main()
{
var baseUrl = #"http://localhost:8080/";
using (WebApp.Start<Startup>(baseUrl))
{
Process.Start(baseUrl + "index.html");
Console.WriteLine("Server running at " + baseUrl);
Console.ReadLine();
}
}
public class Startup
{
public readonly string Html = #"
<!DOCTYPE html>
<html>
<head>
<title>SignalR Simple Chat</title>
</head>
<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 src='https://code.jquery.com/jquery-1.6.4.js'></script>
<script src='http://ajax.aspnetcdn.com/ajax/signalr/jquery.signalr-2.2.0.js'></script>
<script src='http://localhost:8080/signalr/hubs'></script>
<script type='text/javascript'>
$(function () {
$.connection.hub.url = 'http://localhost:8080/signalr';
$.connection.hub.logging = true;
var chat = $.connection.myHub;
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>
</body>
</html>
".Trim();
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
app.Map("/index.html", config =>
{
config.Run(context =>
{
context.Response.ContentType = "text/html";
return context.Response.WriteAsync(Html);
});
});
}
}
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
}
Outcome
If you look at the browser console you see that SignalR eventually exhausts all transports, so can't create a connection between the client and the server
SignalR: Client subscribed to hub 'myhub'.
Negotiating with 'http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22myhub%22%7D%5D'.
serverSentEvents transport starting.
Attempting to connect to SSE endpoint 'http://localhost:8080/signalr/connect?transport=serverSentEvents&clientProt…joHW8a9m5Q%3D%3D&connectionData=%5B%7B%22name%22%3A%22myhub%22%7D%5D&tid=1'.
EventSource connected.
serverSentEvents transport timed out when trying to connect.
EventSource calling close().
serverSentEvents transport failed to connect. Attempting to fall back.
foreverFrame transport starting.
Forever Frame is not supported by SignalR on browsers with SSE support.
foreverFrame transport failed to connect. Attempting to fall back.
longPolling transport starting.
Opening long polling request to 'http://localhost:8080/signalr/connect?transport=longPolling&clientProtocol=…QeRv75joHW8a9m5Q%3D%3D&connectionData=%5B%7B%22name%22%3A%22myhub%22%7D%5D'.
longPolling transport timed out when trying to connect.
Aborted xhr request.
longPolling transport failed to connect. Attempting to fall back.
Fallback transports exhausted.
Stopping connection.
Fired ajax abort async = true.
However this same exact code works without issue within a Console Application
Question
Does anyone know why this code fails when hosting in linqpad but works when hosting in a console application?
I'm leaning towards some sort of connectivity issue rather than a LinqPad one as I'm able to run your code snipped without an issue. I am also running it under version 5. Do you happen to have a 3r party firewall?
Just to confirm, what's the expected behaviour? When I run it, my browser pops up asking me to enter a name. Then I'm able to send 'messages', which appear on the page below the text box.
Related
I am trying to connect to my self-hosted SignalR server remotely using a web-based client. I can successfully connect to the server locally, but I cannot connect to the server remotely.
When I type (self-hosted ip4 address):7777/signalr/hubs in the URL of my remote browser, I see the generated hubs file. Therefore, I believe that I'm making a connection. But I want to see the web page. If you couldn't tell already, I'm a beginner to SignalR
2 more questions:
What URL should I type into the other computer's web browser to connect to my server?
What code determines what that URL is?
Here's my code
The self-hosted SignalR server (a wpf application)
public partial class MainWindow : Window
{
IDisposable SignalR;
private string url = "http://*:7777";
public MainWindow()
{
InitializeComponent();
SignalR = WebApp.Start(url);
Console.WriteLine("Server running on {0}", url);
}
}
Startup class
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
Index Page (scripts)
#section scripts
{
<!--Reference the jQuery library. -->
<script src="~/Scripts/jquery-3.4.1.min.js"></script>
<!--Reference the SignalR library. -->
<script src="~/Scripts/jquery.signalR-2.4.3.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="http://localhost:7777/signalr/hubs"></script>
<!--Add script to update the page and send messages.-->
<script src="~/MyScripts/Main.js"></script>
}
My js file
$(function () {
$.connection.hub.url = "http://localhost:7777/signalr";
var hub = $.connection.mainHub;
$.connection.hub.logging = true;
$.connection.hub.log("Trying to connect...");
// Start connection
$.connection.hub.start()
.done(function () {
$.connection.hub.log('Now connected, connection ID=' + $.connection.hub.id);
});
})
.fail(function () {
$.connection.hub.log("Error!");
});
});
I figured out how to connect remotely to my self-hosted server.
I changed localhost with my ip4 address (192.168.0.183) in the js and index script section
My new code
$.connection.hub.url = "http://192.168.0.183:7777/signalr";
and
<script src="http://192.168.0.183:7777/signalr/hubs"></script>
Finally, I had to edit my applicationhost.config file located at
/{project folder}/.vs/{solution name}/config/applicationhost.config
In the binding protocol section, I changed localhost after the port to my ip4 address
New code
<bindings>
<binding protocol="http" bindingInformation="*:58038:192.168.0.183" />
<binding protocol="https" bindingInformation="*:44389:192.168.0.183" />
</bindings>
This at least allows me to connect to the http url (doesn't work with https in my experience so far)
I am learning SignalR. Using a tutorial I managed to create a very simple ASP.NET MVC based SignalR Server. This is the code of the hub:
[HubName("echo")]
public class EchoHub : Hub
{
public void Say(string message)
{
Trace.WriteLine(message);
}
}
This is my Startup file:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
}
}
Next I created a HTML/JavaScript based client, that was in the same project as the server code. This client works correctly, when I debug the application, the Output window displays my message. This the code of the first client:
<script src="../Scripts/jquery-1.10.2.min.js"></script>
<script src="../Scripts/jquery.signalR-2.1.2.min.js"></script>
<script src="/signalr/hubs"></script>
<script>
$(function() {
var hubProxy = $.connection.echo;
$.connection.hub.logging = true;
$.connection.hub
.start()
.done(function () {
hubProxy.server.say('Hello SignalR');
});
})
</script>
The last thing I wanted to do is to create an client in separate project. I changed a little bit the code, mostly by adding the url's of the server. But this client don't work properly. This is the code of the client:
<script src="Scripts/jquery-1.6.4.min.js"></script>
<script src="Scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="http://localhost:51644/signalr/hubs"></script>
<script>
$(function() {
$.connection.hub.url = 'http://localhost:51644/signalr';
var hubProxy = $.connection.echo;
$.connection.hub.logging = true;
$.connection.hub
.start()
.done(function () {
hubProxy.server.say('Hello SignalR');
});
})
</script>
The JavaScript don't even enters the function in the done method after starting the connection. But in console on Chrome there are no errors or exceptions being thrown.
So the question is what am I doing wrong in the second client?
EDIT: I enabled and checked the SignalR client logs in Chrome console. The second client is stoping by step "SignalR: Negotiating with '/signalr/negotiate?clientProtocol=1.4&connectionData=%5B%5D'." No errors are returned, everything seems alright, but the client cannot go past this step.
EDIT2: According to the comment from JF Beaulieu the negotiate return 200 status code, but nothing else. After the negotiate there should be next steps, like invoking Say, but in the external client hangs on the negotiate. I paste here console outputs from the build in and external JavaScript client.
Build in client output:
External client output:
The second screenshot shows, that the client stops executing on the negotiate step.
I've also tried the solution of Mareq, but adding the jsonp attribute didn't help.
EDIT3: I add here a link to my Github repo with this project, the server is in the AspNetServer project and client in JavaScriptClient project, hope that helps:
https://github.com/RomanSuska/SignalRSandbox
I finally found the solution of my problem and it's stupid simple. The problem was with the SignalR JavaScript file version, on the server I used version 2.1.2 of SignalR and on the client I used 2.2.0. When I switched the version of the client to 2.1.2 everything started to work.
My case was due to my Hub having a constructor because I was trying to do Dependency Injection with Unity. After I removed the constructor, everything worked.
Add this argument to start:
$.connection.hub.start({ jsonp: true });
EDIT I hosted SignalR in console app and I used simple set configuration:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR(new HubConfiguration { EnableJSONP = true });
}
}
and client code I used like this:
$(function () {
var hub = $.connection.serviceHub;
$.connection.hub.url = 'http://mareq.ddns.net:8087/signalr';
$.connection.hub.start({ jsonp: true });
...
});
I want to push a notification from my server to client. I've tried the chat tutorial found here but this just sends chat messages from one client to client
What I want is a button whose onClick method is running at server. Once, I click this button, notifications (a string message) must be sent to all clients.
My Hub class is
public class NotificationsHub : Hub
{
public void SendNotification(string author, string message)
{
Clients.All.broadcastNotification(author, message);
}
}
and in the button click, I try this
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>();
context.Clients.All.SendNotification("Admin", "stop the chat");
But still, I'm not able to notify the clients. Nothing is happening. What am I doing wrong??
My JS at client web page to notify is like this
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var notifications = $.connection.notificationsHub;
// Create a function that the hub can call to broadcast messages.
notifications.client.broadcastNotification = function (name, message) {
alert(name + " says '" + message + "'");
};
You need to call broadcastNotification in your button click function, like below:
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>();
context.Clients.All.broadcastNotification("Admin", "stop the chat");
Also, you need to start the hub by adding $.connection.hub.start(), your JS file should be like:
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery.signalR-2.0.1.js"></script>
<script src="/signalr/hubs"></script>
<script type="text/javascript">
$(function () {
var notifications = $.connection.notificationsHub;
notifications.client.broadcastNotification = function (name, message) {
alert(name + " says '" + message + "'");
$.connection.hub.start();
};
</script>
I'm following "Broadcasting over a Hub from outside of a Hub", but my client browser isn't getting any messages. No errors either. It's like signalR doesn't know about my browser when it comes time to pull the hubContext and send messages.
However, my hub DOES act as expected when calling from client to hub.
My hub:
[HubName("myHub")]
public class MyHub : Hub
{
public void SaySomething(string message)
{
Clients.say(message);
}
public void SayHelloWorld()
{
Clients.say("hello world");
}
}
Code from other place and time in server:
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.say(message);
And my client script:
<script src="Scripts/jquery.signalR-0.5.3.js" type="text/javascript" > </script>
<script src="signalr/hubs"> </script>
<script type="text/javascript">
var hub = $.connection.myHub;
$.extend(hub, {
Say: function(message) {
alert(message); //this only works when the sayHelloWorld() method is executed
}
});
$.connection.hub.start()
.done(function(){
hub.sayHelloWorld(); //this works
});
</script>
If you are using the latest nuget package (v 1.0.1) try:
$.extend(hub.client, {
say: function(message) {
alert(message); //this only works when the sayHelloWorld() method is executed
}
And:
$.connection.hub.start()
.done(function(){
hub.server.sayHelloWorld(); //this works
});
SignalR defined client and server properties for the hub, I was trying the MoveShape sample from a video and faced the same issue.
Hope it helps,
I have created a sample signalR for POC.
I want to call a hub method from Global.asax and pass a string value to client.
My Message hub is :-
[HubName("messageHub")]
public class MessageHub : Hub
{
public static IHubContext context = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
public void Message()
{
/*
* services and updates the messages
* property on the PushMessage hub
*/
//IHubContext context = GlobalHost.ConnectionManager.GetHubContext<SignalR_Error_Logging.Models.ErrorModel>();
List<GenerateError.Repository.ErrorModel> model = ErrorRepository.GetError();
context.Clients.pushMessages(model[0].ErrorMessage);
}
I have defined two of the scripts in layout.cshtml
<script type="text/javascript" src="../../Scripts/jquery-1.6.4.js"></script>
<script type="text/javascript" src="../../Scripts/jquery.signalR-0.5.3.js"></script>
My Index.html is as below:-
#{
ViewBag.Title = "Receive Error message";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script src="/signalr/hubs" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
var myHub = $.connection.messageHub;
myHub.pushMessages = function (value) {
console.log('Server called addMessage(' + value + ')');
$("#messages").append("<li>" + value + "</li>");
};
$("#btnMessage").click(function () {
myHub.message();
});
$.connection.hub.start().done(function () { alert("Now connected!"); }).fail(function () { alert("Could not Connect!"); });
});
</script>
<h2>Receive Error Messages</h2>
<ul id="messages"></ul>
<input type="button" id="btnMessage" value="Get Error" />
In Global.asax
I have written
SignalR_Error_Logging.SignalRHub.MessageHub hub = new SignalRHub.MessageHub();
hub.Message();
In Application_Start();
I am not able to display message in my UI(i.e Index.cshtml).
Things that i have tried:-
Running the application as IIS.
Changing the way of creating HubContext.
IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<MessageHub>();
context.Clients.notify("Hello world");
if (Clients != null)
{
Clients.shootErrorMessage(message);
this.Clients.shootErrorMessage(message);
}
Gone thru links of StackoverflowCalling SignalR hub clients from elsewhere in system
Any advice???
When i call my hub method by creating a button in Index.html it works fine.
Apologies for not framing my question properly!!
I figured out, way of calling the Hub was not correct.
For the time being i have modified my code as in Global.asax :-
private void CallSignalR()
{
var context = SignalR.GlobalHost.ConnectionManager.GetHubContext<SignalR_Error_Logging.SignalRHub.MessageHub>();
List<GenerateError.Repository.ErrorModel> err = GenerateError.Repository.ErrorRepository.GetError();
foreach (var item in err)
{
item.ErrorDescription = item.ErrorDescription + DateTime.Now.ToString();
}
context.Clients.pushMessages(err);
}
Works absolutely fine now :)
Still figuring out better alternatives !!!!
This will never work. Application_Start() in Global.asax is only called once for the lifetime of your AppDomain. It happens when the website starts up and at this point in time, no clients are connected yet (as the website isn't fully initialized) so you can't send messages via SignalR.