Signalr is usually used in "server" side asp.net through hubs and connections. What I'm trying to accomplish (if possible) is using signalr as a "client" in a website.
I have 2 websites, one as a server and another as a client.
I tried this
public void Start()
{
new Task(this.Listen).Start();
//new Thread(this.Listen).Start();
}
private async void Listen()
{
try
{
using (var hubConnection = new HubConnection("http://localhost:8081"))
{
var myHub = hubConnection.CreateHubProxy("mediaHub");
hubConnection.StateChanged += change => System.Diagnostics.Debug.WriteLine(change.OldState + " => " + change.NewState);
myHub.On<string>(
"log",
message =>
System.Diagnostics.Debug.WriteLine(message + " Notified !"));
await hubConnection.Start();
}
}
catch (Exception exc)
{
// ...
}
}
The server is calling the client like this:
var hub = GlobalHost.ConnectionManager.GetHubContext<MediaHub>() ;
hub.Clients.All.log("Server: " + message);
Nothing reaches the client !
As soon as your connection is ready, you actually dispose it, so it does not stay around. As a quick test, remove the using stuff and make your hubConnection a static member of your class, and check if you get called. If you do, then from there you can refine it, but at least you have clearer what's going on.
Related
I encountered connection issue while developing chat application on Android, it uses SignalR Core on the server side. Connection with server is established properly, client invokes for the first time server's method, server then invokes client's method successfully and in the end of execution of Android's listener connection drops. Each subsequent request to the server after the first one needs reconnection, becuase hubConnection.connectionState == HubConnectionState.DISCONNECTED. Reconnecting after each request is obviously bad. SignalR docs don't mention such case.
What am I doing wrong?
Kotlin code:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val hubConnection = HubConnectionBuilder.create("http://192.168.0.171:6000/chathub").build()
hubConnection.start()
hubConnection.on("MessageAdded", {msg ->
chat_view_text.text = msg.from+": "+msg.content
}, ChatMessage::class.java)
chat_view_send_button.setOnClickListener{
if (hubConnection.connectionState == HubConnectionState.CONNECTED){
hubConnection.send("SendMessage", Message("android", "hello world"));
}
}
}
.NET code:
public class ChatHub : Hub<IClientChatActions>, IServerChatActions
{
public override Task OnConnectedAsync()
{
Console.WriteLine("connected");
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception exception)
{
Console.WriteLine("disconnected");
return base.OnDisconnectedAsync(exception);
}
public async Task SendMessage(Message msg)
{
await Clients.All.MessageAdded(msg);
}
}
Android loses the connection while updating the UI this way ...
Try to update your UI in the main thread using Kotlin Coroutines.
Dispatchers.Main is the recommended dispatcher for performing UI-related events.
To do so, add to your build.gradle:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
In your activity.kt file, change it to something like:
private var job: Job = Job()
private val scope = CoroutineScope(job + Dispatchers.Main)
...
hubConnection.on("MessageAdded", {msg ->
scope.launch(Dispatchers.Main) {
chat_view_text.text = msg.from+": "+msg.content
}
}, ChatMessage::class.java)
I'm trying to host a websockets server on Azure. I'm a bit confused and was hoping you could help me.
I've followed many articles but my code is close to the one from this article : https://azure.microsoft.com/en-us/blog/introduction-to-websockets-on-windows-azure-web-sites/
public void ProcessRequest(HttpContext context)
{
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(ProcessWS);
}
}
public bool IsReusable { get { return false; } }
private async Task ProcessWS(AspNetWebSocketContext context)
{
try
{
WebSocket socket = context.WebSocket;
while (true)
{
var url = context.RequestUri;
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);
if (socket.State == WebSocketState.Open)
{
string userMessage = Encoding.UTF8.GetString(buffer.Array, 0, result.Count)
.Trim(new char[] { ' ' }); // We remove the spaces before and after
// DO SOMETHING
}
else
{
break;
}
}
}
catch (Exception e)
{
Log.Info("Exception" + e.Message + " >>>" + e.StackTrace);
}
}
This works fine, I'm able to get the messages from my devices and answer to them.
But in some cases I need to send a message to another device, example :
DEVICE A sends "Tell Device B to blink"
Since it's a websockets server and Device B has already talked with the server I should have somewhere a connection opened with Device B. And when Device A asks me for it I can send a message to Device B.
But how can I achieve that with my code ? How can I find the connection to device B ? If not possible how should I do it ?
I hope my problem is described enough to be understood.
Thank you,
But how can I achieve that with my code ? How can I find the connection to device B ? If not possible how should I do it ?
According to your scenario, I followed this tutorial about webapi-and-websockets and implement my Web API project that could establish connection between clients. Here is the screenshoot for test, you could refer to it:
Additionally, you could leverage SignalR and map your client (user) to signalR connections. For more details, you could refer to Mapping SignalR Users to Connections. Also, you could refer to the git sample Microsoft.AspNet.SignalR.Samples.
UPDATE:
How would you use signalR in that case ?
For a simple way, we could retain connection and user info stored in memory, For more details, you could refer to In-memory storage. Based on this scenario, I wrote a sample project AspDotNet-SignalR-Chat, you could refer to it, and here is the screenshot for test:
I'm taking a look at a Firebase library for .NET:
https://github.com/ziyasal/FireSharp
I have a simple ASP.NET MVC site that I'm using to read/write data to Firebase. It's working great, but I'd like to incorporate the streaming listener functionality so I can get notified when data has changed.
What do I need to do in order to engage that? I don't believe it can just hang off an action method and be notified, correct?
EventStreamResponse response = await _client.OnAsync("chat", (sender, args) => {
System.Console.WriteLine(args.Data);
});
//Call dispose to stop listening for events
response.Dispose();
I figured this out for the most part.
In the Global.asax I have a method like below that I just call on App Start. Strange thing I'm not currently sure why is on initial load it calls the "added" method for all elements in the list.
private static async void EventStreaming()
{
EventStreamResponse response = await FirebaseHelper.Client.OnAsync("emails",
added: (sender, args) =>
{
Debug.WriteLine("ADDED " + args.Data + " -> 2\n");
},
changed: (sender, args) =>
{
Debug.WriteLine("CHANGED " + args.Data);
},
removed: (sender, args) =>
{
Debug.WriteLine("REMOVED " + args.Path);
});
//Call dispose to stop listening for events
//response.Dispose();
}
Try with https://www.nuget.org/packages/FirebaseSharp/ it's a good firebase .Net Client
Is there any way to stop SignalR server? I have self-hosted SignalR server as Windows service but i can't find any way to stop server/service.
I tried this but it doesn't work - server keeps listening and prevents service from stopping.
Alternatively, how can i stop service altogether, forcing SignalR shutdown?
[edit]:
Most of source i cannot share (copyright/security) but I'll do my best:
SignalR server init
Task signalRTask = null;
IDisposable SignalR;
#region SignalR server init
// Kreiraj SignalR server
try
{
cancelTokenSrc = new CancellationTokenSource();
signalRTask = Task.Factory.StartNew(RunSignalR, TaskCreationOptions.LongRunning, cancelTokenSrc.Token);
logfile.Info("Starting notifications pool thread...");
//Console.WriteLine(DateTime.Now.ToString("dd.MM.yyyy. HH:mm:ss ") + "Starting notifications pool thread...");
senderThread = new Thread(delegate()
{
sender.poolEvents();
});
senderThread.Start();
}
catch (Exception ex)
{
// greška u startanju SignalR servera
ServiceEngine.logfile.Info("Error starting SignalR on " + signalr_bind + " with error:" + ex.ToString());
//Console.WriteLine(DateTime.Now.ToString("dd.MM.yyyy. HH:mm:ss ") + "Error starting SignalR # {1} with error {0}", ex.ToString(), signalr_bind);
}
#endregion
#region SignalR start
// pokreće signalR server
public void RunSignalR(object task)
{
try
{
logfile.Info("Starting SignalR server on " + signalRBindAddr);
SignalR = WebApp.Start(signalRBindAddr);
logfile.Info("SignalR server running on " + signalRBindAddr);
}
catch (Exception ex)
{
ServiceEngine.logfile.Info("Error starting SignalR: " + ex.ToString());
}
//Console.WriteLine(DateTime.Now.ToString("dd.MM.yyyy. HH:mm:ss ") + "SignalR server running on {0}", signalRBindAddr);
}
#endregion
public class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration { };
hubConfiguration.EnableDetailedErrors = true;
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR(hubConfiguration);
}
}
public void Stop()
{
try
{
logfile.Info("Stop called...");
SignalR.Dispose();
cancelTokenSrc.Cancel();
//signalRTask.Dispose();
logfile.Info("SignalR down, exit...");
//Console.WriteLine(DateTime.Now.ToString("dd.MM.yyyy. HH:mm:ss ") + "SignalR down, exit...");
}
catch (Exception ex)
{
ServiceEngine.logfile.Info(ex.ToString());
}
}
When i call Stop(), SignalR keeps listening (clients can connect, port has status "LISTEN"...) and i can't stop service.
P.S. Sorry for bad formatting and bad English
Not clearly a complete answer, but I managed to achieve a pause, not a stop in my ancient services as explained in Self-Hosting SignalR in a Windows Service.
However, I then refactored all of them to be micro services, placing the hubs which must be stopped in separate micro services. This solution allows you to manually start and stop the services via SCM or via direct invocation if you are running on a container.
Architecturally speaking, the latter solution has a certain overhead as you are adding a new layer which requires authentication, federation and data IO. Depending on the context, you might already have the infrastructure to support that.
I'm trying to set up a specific scenario but, obviously, I'm having problems. My server is a site that primarily hosts a WCF service but I want to add an XSockets host there as well. I have the standard code in the bootstrap code file as per the instructions in the readme.txt. Upon a client connection, I am starting a worker thread which is basically a heartbeat that the client will monitor. The relevant code from the controller is as follows:
public class HeartbeatController : XSocketController
{
public void AddMessage(string message)
{
this.SendToAll(message, "addMessage");
}
}
Within my worker thread I am calling this:
string message = String.Format("pump", Math.Round(cpuCounter.NextValue());
ClientPool connection = ClientPool.GetInstance("ws://mywebsite:4502/HeartbeatController", "*");
connection.Send(message, "addMessage");
Currently I'm testing this with a console client which looks like this:
class Program
{
static XSocketClient socketClient;
static void Main(string[] args)
{
Console.WriteLine("Starting client...");
string url = "ws://mywebsite:4502/HeartbeatController";
socketClient = new XSocketClient(url, "*");
socketClient.OnOpen += socketClient_OnOpen;
socketClient.Open();
while (true)
{
// let it sit and display the "pump" messages
string input = Console.ReadLine();
if (input.Equals("Q", StringComparison.Ordinal))
{
break;
}
}
}
static void socketClient_OnOpen(object sender, EventArgs e)
{
Console.WriteLine("socketClient Opened");
socketClient.Bind("addMessage", OnAddMessage);
}
private static void OnAddMessage(ITextArgs textArgs)
{
Console.WriteLine("AddMessage :: {0}", textArgs.data);
}
}
On the client, if I put a breakpoint in the socketClient_OnOpen method it gets hit so I think it is connecting. But the pump message never makes it to the client.
Two Questions:
Is there anything obvious that I'm missing?
(Unrelated) Since many enterprises really don't like punching holes in their firewalls, is there any way to use port 80 with this setup (so that the client connection would look like "ws://mywebsite/HeartbeatController")?
Thanks for any help!
So to see what your pump actually was sending in to the server I added a custom pipeline.
public class MyPipeline : XSocketPipeline
{
//Incomming textmessage
public override void OnMessage(IXSocketController controller, ITextArgs e)
{
Console.WriteLine("IN " + e.data);
//Let the message continue into the server
base.OnMessage(controller, e);
}
//Outgoing textmessage
public override ITextArgs OnSend(IXSocketProtocol protocol, ITextArgs e)
{
Console.WriteLine("OUT " + e.data);
return base.OnSend(protocol, e);
}
}
Since I then saw that you was sending in a string that actually did not have a property named "message". The actionmethod "AddMessage" expects you to pass in a property message of type string. So you can solve this in two ways, both of them are simple.
Just replace the string parameter in the AddMessage with ITextArgs
public void AddMessage(ITextArgs message)
or...
Pass in a object from your worker thread instead of a string like this
connection.Send(new {message}, "addMessage");
So all you need to do to get it to work is to change this row
connection.Send(message, "addMessage");
with this row
connection.Send(new {message}, "addMessage");
EDIT: Btw, 4.0 is on the way and the client will be very much improved as well as the serverside stuff.