SignalR: Task Join() is not called - client

I'm trying to create a chat application using SignalR. To make it possible to send private messages I want to assign clients to a group with the name of their profileID. So I can simply call the addMessage function of the group to send to a specific client.
When I go to this page: https://github.com/SignalR/SignalR/wiki/Hubs
It tells me to add a function to the Hub called Join(). In here I can add the incomming client to a group. So I created this code:
[HubName("Chat")]
public class ChatHub : Hub
{
public Task Join()
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
Profiel_DataHelper profiel = new Profiel_DataHelper(HttpContext.Current.User.Identity.Name);
return Groups.Add(Context.ConnectionId, profiel.ProfielID.ToString());
}
else
{
return null;
}
}
.....
When I want to call a specific client I use this code:
var context = GlobalHost.ConnectionManager.GetHubContext();
context.Clients.Group(profielidNaar).addTyptOnline(profielidVan);
But when I run the program the Join() Task is not being called at all, therefore my call to the group is also not working.
What am I doing wrong?

Join is a method on your hub that you need to call from the client. Nobody is going to call it for you and "Join" isn't a special method that gets called automatically. The documentation is showing you how to declare method that "can" be called from the client.
There's other ways to know when clients connect, reconnect and disconnect and it's detailed here:
https://github.com/SignalR/SignalR/wiki/Hubs#detecting-connect-reconnect-and-disconnect-clients-in-hubs

Related

Handle data after http get request in angular

I have a service that requests data from a get method, I'd like to map the response to an object storing some Ids and use those Ids to make other http requests.
I was told this isn't usually done in a callback manner, I looked at this How do I return the response from an asynchronous call? but I don't think it's the usual way to implement services, any hints are very appreciated.
Tried adding in onInit/constructor method in angular to be sure the object was filled before other methods were called without success.
#Injectable ()
export class ContactService {
storeIds;
getIds(callback: Function) {
this.http.get<any>(IdsUrl, Config.options).subscribe(res => {
callback(response);
});
getIds(res => {
this.storeIds = {
profileId: res.profile,
refIds: res.refIds
}
}
)
// this.storeIds returns undefined as it's an async call
this.http.post<any>(WebserviceUrl + this.storeIds.profileId , data, headers )
// .....Many other web services that relay on this Ids
}
Just create another service called StoreIdsService. Update the response you get from your first api call 'getIds' in the StoreIdsService. The idea is to have StoreIdsService as singleton service to keep state of your storeIds. You can inject StoreIdsService in anywhere component you want to get the storeIds.
Its one of manyways to share data in angular between components.
Please refer to this answer someone has posted.
How do I share data between components in Angular 2?
You can simply assign the service response to the storeIds property inside the subscribe method. and call the subsequent services inside it if you need.
#Injectable ()
export class ContactService {
storeIds;
getIds() {
this.http.get<any>(IdsUrl, Config.options).subscribe(res => {
this.storeIds = {
profileId: response.profile,
refIds: response.refIds
}
this.otherapicall1();
this.otherapicall2();
});
}

using signalR .net core client

I have set up a signalR website .net core. My function in my hub is:
public async Task Notify(int id) {
await Clients.All.InvokeAsync("Notified", id);
}
I have also tested this with the following js:
let connection = new signalR.HubConnection(myURL);
connection.on('Notified', data => {
console.log(4, data);
});
connection.start();
The js code seems to work fine and I see the log when I try connection.Invoke('Notify').
Now I have a console app that can needs to make the invoke. I am trying this in two ways and don't mind either solution:
1. A mvc controller within the signalR website that can take the id and invoke 'Notified'.
2. Use the client library Microsoft.AspNetCore.SignalR.Client in the console app.
The way 1 I have only done in classic asp.net like this:
GlobalHost.ConnectionManager.GetHubContext(hubName)
But couldn't find a way to do this in .net core.
Way 2 I have used the library and tried this so far:
var con = new HubConnectionBuilder();
con.WithUrl(myURL);
var connection = con.Build();
connection.InvokeAsync("Notify",args[0]).Wait();
This is the closest I have come to create a connection in the same way as the js code. However this code throws a null pointer when calling connection.InvokeAsync. The connection object is not null. It seems to be an internal object that is null. According to the stack trace the exception is thrown when a MoveNext() function is internally called.
Well looks like both are not currently possible. As of now I just used a forced way which is hopefully temporary.
I have created and used the following base class for hubs:
public abstract class MyHub : Hub
{
private static Dictionary<string, IHubClients> _clients = new Dictionary<string, IHubClients>();
public override Task OnConnectedAsync()
{
var c = base.OnConnectedAsync();
_clients.Remove(Name);
_clients.Add(Name, Clients);
return c;
}
public static IHubClients GetClients(string Name) {
return _clients.GetValueOrDefault(Name);
}
}
GlobalHost is gone. You need to inject IHubContext<THub> like in this sample.
This can be a bug in SignalR alpha1. Can you file an issue on https://github.com/aspnet/signalr and include a simplified repro?

calling SignalR hub from WebAPI controller

I'm using MVC 5, Signal R 2.0.1, and WebAPI 2, and have a simple hub set up named ExportHub
public class ExportHub : Hub
{
public void Send(string name, string message)
{
// Call the addNewMessageToPage method to update clients.
Clients.All.addNewMessageToPage(name, message);
}
}
I'm attempting to call this from WebAPI so the UI can be updated.
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ExportHub>();
But within hubContext I don't see any reference to Send or addNewMessageToPage. How do I gain access to the methods within the hub?
You will not be able to call any methods of specified hub. GetHubContext only returns a IHubContext of specified T Hub. Instead of calling Hub methods you should directly invoke method from your web api method to client like
Clients.All.addNewMessageToPage(name, message);
Try to make your Send method static, and then call hubContext.Send(string, string)

Unable to send a json message from android app to chromecast receiver app

Can't send a json message from my android app to the receiver app.
Android App
I've created my custom MessageStream and I'm using this namespace "com.jujuy.chromecast".
Once I get the channel from the session I attach MyCustomMessageStream to it then and call the method to send the message.
MyCustomMessageStream cm = new MyCustomMessageStream();
channel.attachMessageStream(cm);
cm.sendTestMessage("Hello!");
Receiver App
var receiver = new cast.receiver.Receiver(
APP-ID,
["com.jujuy.chromecast"],
"",
5);
var channelHandler = new cast.receiver.ChannelHandler("com.jujuy.chromecast"); // I think it's not necessary to use com.jujuy.chromecast
channelHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
channelHandler.addChannelFactory(receiver.createChannelFactory("com.jujuy.chromecast"));
receiver.start();
// message listener
function onMessage(event) {
document.getElementById("messageLabel").innerHTML = event.message.type;
}
After start the session () I receive this message
"failed to start application: no channel info received"
on onSessionStartFailed() method and the tv screen turns black.
I think something is wrong with the world "com.jujuy.chromecast", I saw in other examples they use cast.receiver.RemoteMedia.NAMESPACE, I'm not sure if I can change it with the namespace used in MyCustomMessageStream.
I saw in TicTacToe example they use a different way to get de CastDevice object than the documentation says. Could be this the problem?
My chromecast is whitelisted and I was able to run many examples without problem.
I used a custom receiver app to test play video and audio. Any idea?
This is most likely because your namespace on the sender doesn't match the namespace on the receiver. You need to make sure you pass it in as a parameter to the constructor for your custom MessageStream.
Here's an example:
public class MyCustomMessageStream extends MessageStream {
private static final String APP_NAMESPACE = "com.jujuy.chromecast";
protected MyCustomMessageStream(){
super(APP_NAMESPACE);
}
public final void sendTestMessage(String message){
// ...
}
//...
}
You shouldn't need to use the remote media namespace to send messages, that's for media playback. Here's a more in depth answer: https://stackoverflow.com/a/18499253/1839298
At first I couldn't get my package namespace to work, you might try a single word namespace, like 'TEST', to see if you can get that working then proceed from there.

SignalR opens new connection for the same user

I am new to SignalR and trying to implement long running result pooling. I have added JS to my ASP.NET MVC app and created bug class.
JS
<script type="text/javascript">
var message= $.connection.messageHub;
$(function () {
message.addMessage = function (htmlstring) {
alert(htmlstring);
};
$.connection.hub.start(function () {
message.longRunningMethod('#HttpContext.Current.Session.SessionID');
});
});
</script>
c# code
[HubName("messageHub")]
public class MessagesHub : Hub
{
public void longRunningMethod(string sessionId)
{
var repeatChecking = 0;
while (repeatChecking < 3000000)
{
Caller.addMessage("Test");
repeatChecking++;
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
}
The code works fine but there is one problem. Every time the same user refresh web page new Hub class is created and new longRunningMethod method is executed. I would like to resume connection and attach to the same hub instance and resume receiving messages. Could anyone explain how to implement this?
Hubs are created and destroyed very frequently so never put any data that you expect to last on them (unless it's static).
I'm not quite sure why you're looking to have a long running method that can take in data (because SignalR is always up and available to take in/handle data) but here's how you can do it:
Checkout the SignalR stock ticker example (you can pull it in via Nuget). It creates a single instance class that fires up a timer. That timer is used to broadcast data down to the clients. https://github.com/SignalR/SignalR-StockTicker
You can also check out ShootR. It's a multiplayer game built with SignalR that does much of the same. Creates a background timer that acts as the game loop on the server and then pushes data down to clients. https://github.com/NTaylorMullen/ShootR
Ultimately your solution will involve either making a singleton or a static timer that acts as your "long" running method.
Hope this helps!

Resources