I have implemented SignalR for my Windows Azure project. I have two clients - Javascript/HTML client in my web role and a console application in my project. And Web role is my SignalR server. When i put the web role and the console application as the start up projects, the messages i send from the HTML client are sent to the console application. But when i put the Cloud project and the console application as the start up projects, the messages from the HTML client are not being sent to the console application. Its really weird, i dont know what could be the difference between the two which is causing the problem.
And if i put a background thread in my web role which will send messages to connected clients periodically, it works on both occasions, i mean the console app and the HTML client are receiving messages irrespective of the start up projects.
Please let me know if you have any idea what the problem is
My Hub:
public class BroadcastHub : Hub
{
public void Send(PersistedAudioRecord record)
{
// Call the BroadcastAudio method to update clients.
Clients.All.BroadcastAudio(record);
}
}
My HTML/Javascript client:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var broadcast = $.connection.broadcastHub;
// Create a function that the hub can call to broadcast messages.
broadcast.client.broadcastAudio = function (record) {
// Html encode user name, channel and title.
var encodedName = $('<div />').text(record.Username).html();
var encodedChannel = $('<div />').text(record.Channel).html();
var encodedTitle = $('<div />').text(record.Title).html();
// Add the broadcast to the page.
$('#broadcasts').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedChannel + '</strong>: ' + encodedTitle + '</li>');
};
// Get the user name.
$('#displayname').val(prompt('Enter your name:', ''));
// Get the Channel name to which you want to broadcast.
$('#channelname').val(prompt('Enter Channel:', ''));
// Set initial focus to message input box.
$('#title').focus();
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendbroadcast').click(function () {
// Call the Send method on the hub.
var broadcastMessage = {}
broadcastMessage.Username = $('#displayname').val();
broadcastMessage.Channel = $('#channelname').val();
broadcastMessage.Title = $('#title').val();
broadcast.server.send(broadcastMessage);
// Clear text box and reset focus for next broadcast.
$('#title').val('').focus();
});
});
});
</script>
My Console app client:
class Program
{
static void Main(string[] args)
{
HubConnection connection = new HubConnection("http://localhost:35540/");
IHubProxy proxy = connection.CreateHubProxy("BroadcastHub");
proxy.On<AudioRecord>("BroadcastAudio", BroadcastAudio);
connection.Start().Wait();
Console.ReadLine();
}
static void BroadcastAudio(AudioRecord record)
{
Console.WriteLine("Broadcast: {0} {1} {2}", record.Username, record.Channel, record.Title);
}
}
Background Thread:
public class BackgroundThread
{
private static Random _random = new Random();
public static void Start()
{
ThreadPool.QueueUserWorkItem(_ =>
{
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<BroadcastHub>();
while (true)
{
PersistedAudioRecord record = new PersistedAudioRecord();
record.Channel = _random.Next(10).ToString();
record.Username = new string('a', Convert.ToInt32(record.Channel));
record.Title = new string('b', Convert.ToInt32(record.Channel));
try
{
hubContext.Clients.All.BroadcastAudio(record);
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError("SignalR error thrown: {0}", ex);
}
Thread.Sleep(TimeSpan.FromSeconds(2));
}
});
}
}
I tried this scenario with my application and I was able to send messages from a webrole to a console application. Is it possible to zip your project and send it to see if this reproes...
Related
I have the Http server (written in D lang) with endpoint for POST method. I want to stream some commands to that method from my non web client written in .Net, and durring that streaming it would be good for me to listen also for response from this server in case something went wrong. And here is the problem, it seems that I should end streaming (or send zero tcp message) to get any response.
Thanks for Wireshark I know that it behaves like this:
I send message to server with headers
Server answer me with ACK
Next, I send my message with some coordinates
"18\r\n[{"x":0.5,"y":0.5,"z":0}\r\n"
Server answer with ACK and HTTP Continue status
Then I stream some commands to server and it answer me with ACK for each command
Next, I wait a little bit, the server send me HTTP Request Time-out.
BUT, my application will not get this and would think that everything is fine.
One version of implementation:
public async Task Start()
{
httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.TransferEncodingChunked = true;
var json = GetData();
StreamWriter writer = null;
var content = new PushStreamContent(async (stream, httpContent, transportContext) =>
{
writer = new StreamWriter(stream);
writer.AutoFlush = true;
await writer.WriteLineAsync("[" + json);
});
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var message = new HttpRequestMessage(HttpMethod.Post, url);
message.Content = content;
Task.Run(async () =>
{
var result = await httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead);
Console.WriteLine(result.StatusCode);
});
while (true)
{
if(Console.ReadKey().KeyChar == 'a')
{
await writer.WriteLineAsync($",{json}");
}
}
}
Your scenario looks a good match for SignalR.
Basically, SignalR is an open-source library that simplifies adding real-time web functionality to apps. Real-time web functionality enables server-side code to push content to clients instantly.
You can find a sample using SignalR 2 with non-Core version bellow:
First, create the following basic structure in server side:
public class ChatHub : Hub
{
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
Now you need to create the following structure in client side:
<!--Script references. -->
<!--Reference the jQuery library. -->
<script src="Scripts/jquery-3.1.1.min.js" ></script>
<!--Reference the SignalR library. -->
<script src="Scripts/jquery.signalR-2.2.1.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<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>
You can have more info about SignalR here.
So I have a C# WebApi Signalr webservice with a Angular 2 Signalr Client.
The WebService is supposed to update the client when a new message comes in from a post method that other services may call.
[HttpPost]
public void NotificationMessage([FromBody]ServiceInformation serviceInformation)
{
Messages.notificationMessage = serviceInformation;
notificationMessage.BroadCastNotificationMessage();
}
Another service posts to this method and this method sets a static variable in the Messages class and then calls the NotificationMessage Hub.
public class NotificationMessage : Hub<IClient>
{
public void BroadCastNotificationMessage()
{
Clients.All.serviceInfoBroadCast(JsonConvert.SerializeObject(Messages.notificationMessage));
}
}
The notification hub then calls Clients.All to broadcast the new message to all clients.
Angular Site:
Service:
constructor() {
this.connectionEstablished = new EventEmitter<Boolean>();
this.messageReceived = new EventEmitter<ServiceInformationObject>();
this.connectionExists = false;
this.connection = $.hubConnection(CONFIGURATION.baseUrls.server);
this.proxy = this.connection.createHubProxy(this.proxyName);
this.registerOnServerEvents();
this.startConnection();
}
private startConnection(): void {
this.connection.start({ jsonp: true, waitForPageLoad: false}).done((data: any) => {
console.log('Now connected ' + data.transport.name + ', connection ID= ' + data.id);
this.connectionEstablished.emit(true);
this.connectionExists = true;
//this.proxy.invoke('BroadCastNotificationMessage');
}).fail((error: any) => {
console.log('Could not connect ' + error);
this.connectionEstablished.emit(false);
});
}
private registerOnServerEvents(): void {
this.proxy.on('serviceInfoBroadCast', (data: string) => {
console.log('received in SignalRService: ' + JSON.stringify(data));
let jsonData = JSON.parse(data);
let newData = new ServiceInformationObject(jsonData.ServiceName, jsonData.Message, jsonData.State, jsonData.MachineName, Date.now());
this.messageReceived.emit(newData);
});
}
I setup the hub connection followed by the hub proxy. I call the proxy.on method connecting to the Clients.All dynamic method from the web service. Then I start the connection.
Component to display data on view:
private subscribeToEvents(): void {
this._signalRService.connectionEstablished.subscribe(() => {
this.canSendMessage = true;
});
this._signalRService.messageReceived.subscribe((message: ServiceInformationObject) => {
this._ngZone.run(() => {
this.testArray.push(message);
});
});
}
The issue:
If I leave the invoke call in the startConnection method, it will pull data down from the webservice, but it will never update ever again.
If I don't use the invoke nothing happens.
I'm not sure why the webservice is not pushing the information when Clients.All is called. I know the post method is being called from my own interal logging and I know the object is not empty.
Any ideas on why the webservice is not pushing the information? Or why the client is not displaying it?
I have setup a small project composed by a Console Application (my messages Hub) and a Web Application (my messages receiver).
The idea is that the Console Application listen to a RabbitMQ queue and every time a message is received, it broadcast the message to all SignalR Clients connected.
I initialize the Console App in this way:
// start Mass Transit Bus
var busControl = BuildBus();
busControl.Start();
// Start SignalR
string url = "http://localhost:9090";
using (WebApp.Start(url))
{
Console.WriteLine("SignalR Server running on {0}", url);
Console.ReadLine();
}
Then I have my Startup class and my Hub Class as following:
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
}
Now is where I get lost.
Question 1
I want from my Web Application to receive messages, so I initialize the proxy and then?
<script type="text/javascript">
$(function () {
//Set the hubs URL for the connection
$.connection.hub.url = "http://localhost:9090/signalr";
// Declare a proxy to reference the hub.
var chat = $.connection.myHub;
// Declare a Message handler
});
</script>
Question 2
From the Console Application, how do I broadcast a message to all Clients?
Answer 1 You should define client methods as chat.client.someMethod = function(someParams). In your case this client method is chat.client.addMessage = function (name, message) {}.
Answer 2 If you want broadcast some message without connection to your hub from .NET application, then you can do this in this way: GlobalHost.ConnectionManager.GetHubContext<MyHub>().Clients.All.addMessage(/*method params*/).
This is my HTML:
<script type="text/javascript">
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.khaosHub;
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (message) {
// Html encode display name and message.
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li>' + encodedMsg + '</li>');
};
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
console.log("sending");
// Call the Send method on the hub.
chat.server.send("something");
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
</script>
My Hub:
public class KhaosHub : Hub
{
public void Send(string message)
{
Clients.All.broadcastMessage(message);
}
}
When I click #sendmessage my Send method in KhaosHub is triggered which I have verified using a breakpoint and my message does get sent to the div via broadcastMessage.
Note: I've not included my call to app.MapSignalR in the example above as I know it's working from the client side.
The issue I have is when I call broadcastMessage from some back end code it doesn't work. I am calling it via:
var context = GlobalHost.ConnectionManager.GetHubContext<KhaosHub>();
context.Clients.All.broadcastMessage("some message");
When I debug the Clients.All property, I can't see any clients (I don't know if I should be able to but thought I'd add that information.
Any ideas?
EDIT: This is my startup class for the hub:
[assembly: OwinStartup(typeof (Startup))]
namespace CC.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR();
}
}
}
Thanks for the question. Following up on the comments I have tracked my own problem down also to not getting the correct hubcontext from the GlobalHost.ConnectionManager.
To solve this I specifically set a DependencyResolver on the GlobalHost and passing this Resolver to the HubConfiguration used to MapSignalR.
In code that is:
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver =
New Microsoft.AspNet.SignalR.DefaultDependencyResolver
app.MapSignalR(
New Microsoft.AspNet.SignalR.HubConfiguration With
{.Resolver = Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver})
You may want to convert this VB.Net code to C#.
I was using Alchemy websockets for both my client and server but ran into a problem with corrupted/dropped messsages. So I'm trying out another server side implementation. I implemented the server using Fleck, and when I send messages using javascript, the server receives all the messages, solving my previous problem.
However, I need to be able to send messages to the websocket server from a C# client also. Since Fleck does not have a client side implementation in C#, I thought I'd stick with Alchemy. I left the client-side code unchanged so I thought it should just connect to the server as before, however, no messages are being received (though they are being sent according to the debugger).
Here is my server side implementation (Fleck):
private void OnStartWebSocketServer()
{
var server = new WebSocketServer("ws://localhost:11005");
server.Start(socket =>
{
socket.OnOpen = () => Console.WriteLine("Open!");
socket.OnClose = () => Console.WriteLine("Close!");
socket.OnMessage = message => OnReceive(message);
});
}
private static void OnReceive(String message)
{
UpdateUserLocation(message);
}
Here is my client side implementation (Alchemy):
class WSclient
{
WebSocketClient aClient;
public WSclient(String host, String port)
{
aClient = new WebSocketClient("ws://" + host + ":" + 11005 + "/chat")
{
OnReceive = OnReceive,
OnSend = OnSend,
OnConnect = OnConnected,
OnConnected = OnConnect,
OnDisconnect = OnDisconnect
};
aClient.Connect();
}
...
public void Send(String data)
{
aClient.Send(data);
}
I thought it might have something to do with the fact that the Alchemy client requires a channel at the end of the connection string '/chat'. However leaving it blank, or just the '/' gives an error.