I am trying to separate the MassTransit demo (https://masstransit-project.com/MassTransit/quickstart.html) into two separate applications, but my consumer application is not receiving any messages.
I have created 3 projects: Send, Receive, and Messages. Send contains a Send class which is my producer code, Receive contains a Receive class which is my consumer code, and Messages contains classes for my messages.
Here is my Send class:
using MassTransit;
using Messages;
using System;
namespace MassTransitTest
{
class Send
{
static void Main(string[] args)
{
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost"), c =>
{
c.Username("guest");
c.Password("guest");
});
});
bus.Start();
Console.WriteLine("Publishing message");
bus.Publish(new TextMessage() { Text = "Testing 12345" });
bus.Stop();
Console.ReadLine();
}
}
}
here is my Receive class:
using MassTransit;
using Messages;
using System;
namespace Receive
{
class Receive
{
static void Main(string[] args)
{
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost"), c =>
{
c.Username("guest");
c.Password("guest");
});
config.ReceiveEndpoint(host, "queue", endpoint =>
{
endpoint.Handler<TextMessage>(context =>
{
return Console.Out.WriteLineAsync($"{context.Message.Text}");
});
});
});
bus.Start();
Console.WriteLine("Receive listening for messages");
Console.ReadLine();
bus.Stop();
}
}
}
Finally, here is my TextMessage class:
using System;
namespace Messages
{
public class TextMessage
{
public string Text { get; set; }
}
}
When I send the message from my Send class I would like to receive it and output it to the console from my Receive class.
I figured it out! Publish is an asynchronous message that returns a Task, so I needed to await and then everything worked. My Send program was exiting before the message was fired off.
Related
Considering that openvpn is a udp server running on port 1194, I am intrested to see , how can I send a packet using udp c#.
I am aware of the fact that openVpn is a protocol itself, and considering the lack of knowledge in that domain , I started writing a simple udp client to send a message to server.
Here is the code.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ConnectOpenVPN
{
public struct Received
{
public IPEndPoint Sender;
public string Message;
}
abstract class UdpBase
{
protected UdpClient Client;
protected UdpBase()
{
Client = new UdpClient();
}
public async Task<Received> Receive()
{
var result = await Client.ReceiveAsync();
return new Received()
{
Message = Encoding.ASCII.GetString(result.Buffer, 0, result.Buffer.Length),
Sender = result.RemoteEndPoint
};
}
}
//Client
class UdpUser : UdpBase
{
private UdpUser() { }
public static UdpUser ConnectTo(IPAddress serverIP, int port)
{
var connection = new UdpUser();
connection.Client.Connect(serverIP, port);
return connection;
}
public void Send(string message)
{
var datagram = Encoding.ASCII.GetBytes(message);
Client.Send(datagram, datagram.Length);
}
}
class Program
{
static void Main(string[] args)
{
//create a new client
var client = UdpUser.ConnectTo(IPAddress.Parse("13.23.118.17"), 1194);
Task.Run(async () => {
while (true)
{
try
{
var received = await client.Receive();
Console.WriteLine(received.Message);
if (received.Message.Contains("quit"))
break;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
});
string read;
do
{
read = Console.ReadLine();
client.Send(read);
} while (read != "quit");
}
}
}
So , I am unable to recieve any message from server and just witing for ever. Not even an denied or exception.
I was planning to send credentials and openvpn config next.
What are the thing I should do when creating client program for open vpn client ? Is creating an UDP client even correct or I should be using openVPn protocol itseld and how?
I'm very close to implementing a system in C# which allows me to send a push-notification with the click of a button from an interface written in ASP.net to a C# console application acting as client. This all using WebSockets.
After reading a lot of tutorials and reusing code found online I'm already able to successfully establish a WebSocket connection. I'm yet not able to actually send a Notification.
The part I'm struggling with is the function that get's triggered as soon as a button is clicked:
//Close ticket and send push-notification over websocket
public void Close(int id) {
//Ticket ticket = mgr.GetTicket(id);
//Create a new notification
Notification notif = new Notification();
notif.message = "Rofl test123 Notification lol";
//Initialize WebSocketMiddleware here??
//WebSocketsMiddleware wsm = new WebSocketsMiddleware(what parameter??);
//wsm.Invoke(what HttpContext parameter???)
NotificationManager notifMgr;
//notifMgr.AddSubscriber(wsm);
//notifMgr.SendNotificationAsync(notif);
return;
}
The specific questions/problems I'm encountering are:
How to initialize the class WebSocketsMiddleware? Does it need to be initialized, if yes, what is the parameter with type RequestDelegate? What do I pass to that parameter?
WebSocketsMiddleware has an Invoke function with parameter context of type HttpContext. Do I just need to pass new HttpContext() to this? Is that sufficient?
Someone made a class NotificationManager, this class uses the middleware to actually send a notification. Do I just need to pass the initialized WebSocketsMiddleware variable as parameter for NotificationManager.AddSubscriber()? Will the notifications of each client be nicely separated then?
Can I after that just use SendNotificationAsync() to send the notification?
Bonus question: Say that each client has it's own button. When I click a client's button only that client may receive a push-notification. How to make sure that all the other client's don't receive the same notification as well?
To be able to help me with these questions you'll need the following classes. The question is merely about WebSockets but more about how to initiate and use the classes I gathered from the tutorials.
Notification.cs - Class representing a Notification (notification text, send-date,...):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SC.UI.MVC.Models
{
public class Notification
{
public Guid? notificationId { get; set; }
public int id { get; set; }
public DateTime timestamp { get; set; }
public string message { get; set; }
public string type { get; set; }
public Notification()
{
// add a new guid as a unique identifier for the notification in the db
notificationId = Guid.NewGuid();
}
}
}
WebSocketsMiddleware.cs - Has the low-level part of the WebSockets handled, invoking connection etc:
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
namespace NotificationsApi.Notifications
{
public class WebSocketsMiddleware
{
// private variable to track the next delegate to call in the request chain
private readonly RequestDelegate _next;
public WebSocketsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
CancellationToken ct = context.RequestAborted;
string currentSubscriberId = null;
WebSocket currentSocket = null;
// we want to listen on a specific path for websocket communications
if (context.Request.Path == "/notifications/ws")
{
// make sure the request is a websocket request
if (context.WebSockets.IsWebSocketRequest)
{
currentSocket = await context.WebSockets.AcceptWebSocketAsync();
currentSubscriberId = NotificationManager.Instance.AddSubscriber(currentSocket);
// keep the socket open until we get a cancellation request
while (true)
{
if (ct.IsCancellationRequested)
{
break;
}
}
}
else // return an HTTP bad request status code if anything other a web socket request is made on this URI
{
context.Response.StatusCode = 400;
}
}
// clean up the socket
if (!string.IsNullOrWhiteSpace(currentSubscriberId))
{
NotificationManager.Instance.RemoveSubscriber(currentSubscriberId);
if (currentSocket != null)
{
await currentSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
currentSocket.Dispose();
}
}
// call the next delegate in the pipeline
await _next(context);
return;
}
}
}
NotificationManager.cs - Interface/Class with three functions to add and remove subscribers, and to actually send a notification. Uses the WebSocket middleware to achieve this:
using SC.UI.MVC.Models;
//using NotificationsApi.Persistence;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NotificationsApi.Notifications
{
// interface for NotificationManager for dependency injection
public interface INotificationManager
{
string AddSubscriber(WebSocket subscriber);
void RemoveSubscriber(string subscriberId);
Task SendNotificationAsync(Notification notification);
}
public class NotificationManager : INotificationManager
{
// static instance of the NotificationManager class
private static INotificationManager _instance;
public static INotificationManager Instance { get { return _instance ?? (_instance = new NotificationManager()); } set { _instance = value; } }
// static dictionary to keep track of all notification subscribers
private static ConcurrentDictionary<string, WebSocket> _subscribers = new ConcurrentDictionary<string, WebSocket>();
// adds a subscriber to receive notifications
public string AddSubscriber(WebSocket subscriber)
{
var subscriberId = Guid.NewGuid().ToString();
_subscribers.TryAdd(subscriberId, subscriber);
return subscriberId.ToString();
}
// removes a notifications subscriber
public void RemoveSubscriber(string subscriberId)
{
WebSocket empty;
_subscribers.TryRemove(subscriberId, out empty);
}
// sends a notification to all subscribers
public async Task SendNotificationAsync(Notification notification)
{
// add the notification to the persistence store
//await PersistenceManager.Instance.AddNotificationAsync(notification);
// send the notification to all subscribers
foreach (var s in _subscribers)
{
if (s.Value.State == WebSocketState.Open)
{
var jsonNotification = JsonConvert.SerializeObject(notification);
await SendStringAsync(s.Value, jsonNotification);
}
}
}
// sends a string via web socket communication
private async Task SendStringAsync(WebSocket socket, string data, CancellationToken ct = default(CancellationToken))
{
var buffer = Encoding.UTF8.GetBytes(data);
var segment = new ArraySegment<byte>(buffer);
await socket.SendAsync(segment, WebSocketMessageType.Text, true, ct);
}
}
}
Client.cs - Client receiving the push-notification. Not really a problem here I guess:
/* WEBSOCKET PART */
//Variables for websocket
private static object consoleLock = new object();
private const int sendChunkSize = 256;
private const int receiveChunkSize = 256;
private const bool verbose = true;
private static readonly TimeSpan delay = TimeSpan.FromMilliseconds(30000);
//Function to check if a ticket from this client is closed/solved
public void checkTicketSolved() {
Thread.Sleep(1000);
Connect("ws://localhost:5050/notifications/ws").Wait();
Console.WriteLine("Press any key to exit...");
}
public static async Task Connect(string uri)
{
ClientWebSocket webSocket = null;
try
{
webSocket = new ClientWebSocket();
await webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
await Task.WhenAll(Receive(webSocket), Send(webSocket));
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex);
}
finally
{
if (webSocket != null)
webSocket.Dispose();
Console.WriteLine();
lock (consoleLock)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("WebSocket closed.");
Console.ResetColor();
}
}
}
static UTF8Encoding encoder = new UTF8Encoding();
private static async Task Send(ClientWebSocket webSocket)
{
//byte[] buffer = encoder.GetBytes("{\"op\":\"blocks_sub\"}"); //"{\"op\":\"unconfirmed_sub\"}");
byte[] buffer = encoder.GetBytes("{\"op\":\"unconfirmed_sub\"}");
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
while (webSocket.State == WebSocketState.Open)
{
LogStatus(false, buffer, buffer.Length);
await Task.Delay(delay);
}
}
private static async Task Receive(ClientWebSocket webSocket)
{
byte[] buffer = new byte[receiveChunkSize];
while (webSocket.State == WebSocketState.Open)
{
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
else
{
LogStatus(true, buffer, result.Count);
}
}
}
private static void LogStatus(bool receiving, byte[] buffer, int length)
{
lock (consoleLock)
{
Console.ForegroundColor = receiving ? ConsoleColor.Green : ConsoleColor.Gray;
//Console.WriteLine("{0} ", receiving ? "Received" : "Sent");
if (verbose)
Console.WriteLine(encoder.GetString(buffer));
Console.ResetColor();
}
}
}
You can also find this code on Github.
The relevant parts of the code are located in:
WebServer/UI-MVC/Controllers/TicketController.cs -> Contains function triggered when a button is clicked.
WebServer/UI-MVC/Notifications -> Contains NotificationManager.cs and WebSocketsMiddleware.cs
WebServer/UI-MVC/Models -> Contains Notification.cs
Client/ contains all the code for the client's console application
To give you some context about the application:
This application represents a ticketing system which allows clients/customers which use my software to open support tickets. The WebServer-part is for administrators/employees of me to answer and manage tickets. The Console Application is what my customers/clients need to have installed in order to contact my support service and open a support ticket. When an administrator closes the ticket of a client by clicking a button, it means the ticket and thus the client's problem was resolved and closed. Resulting in the client getting a push-notification about that.
I am not looking for references to other tutorials about WebSockets or suggestions using SignalR instead or whatever, I've already read all of them and I've already used SignalR but am interested in pure WebSockets now. I would be very grateful for someone who could help me working out the first part of code posted in this question (the Close-function) and explains what he has done. Thanks!
I found the solution myself.
First I made a new controller called NotificationsController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using SC.UI.MVC.Models;
using NotificationsApi.Notifications;
//using NotificationsApi.Persistence;
using System.Net.Http;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace NotificationsApi.Controllers
{
[Route("api/notifications")]
public class NotificationsController : Controller
{
// GET api/notifications
[HttpGet]
public ActionResult Get()
{
try
{
var notifications = new List<Notification>();
//notifications = PersistenceManager.Instance.GetNotifications();
return Ok(notifications);
}
catch (Exception exception)
{
// log exception
// TODO: implement logging
// return a 500
return StatusCode(500);
}
}
// POST api/notifications
[HttpPost]
public async Task<ActionResult> Post(string message)
{
Notification notification = new Notification();
notification.message = message;
Console.WriteLine(message);
try
{
// return a 400 if we didn't get a valid json payload in the body
if (notification == null)
return BadRequest();
await NotificationManager.Instance.SendNotificationAsync(notification);
// we aren't returning the object to reference because POSTing a notification is fire and forget
return Created(string.Empty, null);
}
catch (Exception exception)
{
// log the error
// TODO: implement logging
// return a 500
return StatusCode(500);
}
return Ok();
}
}
}
Then I commented out the unnecessary properties in Notification.cs so only Guid and message remain. Now I'm able to just send a notification by calling the NotificationsController with a POST request carrying the message-parameter as data.
I have one .NET 4.5.2 Service Publishing messages to RabbitMq via MassTransit.
And multiple instances of a .NET Core 2.1 Service Consuming those messages.
At the moment competing instances of the .NET core consumer service steal messages from the others.
i.e. The first one to consume the message takes it off the queue and the rest of the service instances don't get to consume it.
I want ALL instances to consume the same message.
How can I achieve this?
Publisher Service is configured as follows:
builder.Register(context =>
{
MessageCorrelation.UseCorrelationId<MyWrapper>(x => x.CorrelationId);
return Bus.Factory.CreateUsingRabbitMq(configurator =>
{
configurator.Host(new Uri("rabbitmq://localhost:5671"), host =>
{
host.Username(***);
host.Password(***);
});
configurator.Message<MyWrapper>(x => { x.SetEntityName("my.exchange"); });
configurator.Publish<MyWrapper>(x =>
{
x.AutoDelete = true;
x.Durable = true;
x.ExchangeType = true;
});
});
})
.As<IBusControl>()
.As<IBus>()
.SingleInstance();
And the .NET Core Consumer Services are configured as follows:
serviceCollection.AddScoped<MyWrapperConsumer>();
serviceCollection.AddMassTransit(serviceConfigurator =>
{
serviceConfigurator.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://localhost:5671"), hostConfigurator =>
{
hostConfigurator.Username(***);
hostConfigurator.Password(***);
});
cfg.ReceiveEndpoint(host, "my.exchange", exchangeConfigurator =>
{
exchangeConfigurator.AutoDelete = true;
exchangeConfigurator.Durable = true;
exchangeConfigurator.ExchangeType = "topic";
exchangeConfigurator.Consumer<MyWrapperConsumer>(provider);
});
}));
});
serviceCollection.AddSingleton<IHostedService, BusService>();
And then MyWrapperConsumer looks like this:
public class MyWrapperConsumer :
IConsumer<MyWrapper>
{
.
.
public MyWrapperConsumer(...) => (..) = (..);
public async Task Consume(ConsumeContext<MyWrapper> context)
{
//Do Stuff
}
}
It sounds like you want to publish messages and have multiple consumer service instances receive them. In that case, each service instance needs to have its own queue. That way, every published message will result in a copy being delivered to each queue. Then, each receive endpoint will read that message from its own queue and consume it.
All that excessive configuration you're doing is going against what you want. To make it work, remove all that exchange type configuration, and just configure each service instance with a unique queue name (you can generate it from host, machine, whatever) and just call Publish on the message producer.
You can see how RabbitMQ topology is configured: https://masstransit-project.com/advanced/topology/rabbitmq.html
Thanks to the Answer from Chris Patterson and the comment from Alexey Zimarev I now believe I have this working.
The guys pointed out (from my understanding, correct me if I am wrong) that I should get rid of specifying the Exchanges and Queues etc myself and stop being so granular with my configuration.
And let MassTransit do the work in knowing which exchange to create & publish to, and which queues to create and bind to that exchange based on my type MyWrapper. And my IConsumerimplementation type MyWrapperConsumer.
Then giving each consumer service its own unique ReceiveEndpoint name we will end up with the exchange fanning out messages of type MyWrapper to each unique queue which gets created by the unique names specified.
So, in my case..
THE PUBLISHER SERVICE config relevant lines of code changed FROM:
configurator.Message<MyWrapper>(x => { x.SetEntityName("my.exchange"); });
configurator.Publish<MyWrapper>(x =>
{
x.AutoDelete = true;
x.Durable = true;
x.ExchangeType = true;
});
TO THIS
configurator.Message<MyWrapper>(x => { });
configurator.AutoDelete = true;
AND EACH CONSUMERS SERVICE instance config relevant lines of code changed FROM:
cfg.ReceiveEndpoint(host, "my.exchange", exchangeConfigurator =>
{
exchangeConfigurator.AutoDelete = true;
exchangeConfigurator.Durable = true;
exchangeConfigurator.ExchangeType = "topic";
exchangeConfigurator.Consumer<MyWrapperConsumer>(provider);
});
TO THIS:
cfg.ReceiveEndpoint(host, Environment.MachineName, queueConfigurator =>
{
queueConfigurator.AutoDelete = true;
queueConfigurator.Consumer<MyWrapperConsumer>(provider);
});
Note, the Environment.MachineName gives the unique queue name for each instance
We can achieve it by having separate queue for each consumer services and each queue bind with a single exchange. When we publish message to exchange it will send copy of message to each queue and eventually received by each consumer services.
Messages :
namespace Masstransit.Message
{
public interface ICustomerRegistered
{
Guid Id { get; }
DateTime RegisteredUtc { get; }
string Name { get; }
string Address { get; }
}
}
namespace Masstransit.Message
{
public interface IRegisterCustomer
{
Guid Id { get; }
DateTime RegisteredUtc { get; }
string Name { get; }
string Address { get; }
}
}
Publisher Console App :
namespace Masstransit.Publisher
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("CUSTOMER REGISTRATION COMMAND PUBLISHER");
Console.Title = "Publisher window";
RunMassTransitPublisher();
}
private static void RunMassTransitPublisher()
{
string rabbitMqAddress = "rabbitmq://localhost:5672";
string rabbitMqQueue = "mycompany.domains.queues";
Uri rabbitMqRootUri = new Uri(rabbitMqAddress);
IBusControl rabbitBusControl = Bus.Factory.CreateUsingRabbitMq(rabbit =>
{
rabbit.Host(rabbitMqRootUri, settings =>
{
settings.Password("guest");
settings.Username("guest");
});
});
Task<ISendEndpoint> sendEndpointTask = rabbitBusControl.GetSendEndpoint(new Uri(string.Concat(rabbitMqAddress, "/", rabbitMqQueue)));
ISendEndpoint sendEndpoint = sendEndpointTask.Result;
Task sendTask = sendEndpoint.Send<IRegisterCustomer>(new
{
Address = "New Street",
Id = Guid.NewGuid(),
RegisteredUtc = DateTime.UtcNow,
Name = "Nice people LTD"
}, c =>
{
c.FaultAddress = new Uri("rabbitmq://localhost:5672/accounting/mycompany.queues.errors.newcustomers");
});
Console.ReadKey();
}
}
}
Receiver Management console app :
namespace Masstransit.Receiver.Management
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Management consumer";
Console.WriteLine("MANAGEMENT");
RunMassTransitReceiver();
}
private static void RunMassTransitReceiver()
{
IBusControl rabbitBusControl = Bus.Factory.CreateUsingRabbitMq(rabbit =>
{
rabbit.Host(new Uri("rabbitmq://localhost:5672"), settings =>
{
settings.Password("guest");
settings.Username("guest");
});
rabbit.ReceiveEndpoint("mycompany.domains.queues.events.mgmt", conf =>
{
conf.Consumer<CustomerRegisteredConsumerMgmt>();
});
});
rabbitBusControl.Start();
Console.ReadKey();
rabbitBusControl.Stop();
}
}
}
Receiver Sales Console app:
namespace Masstransit.Receiver.Sales
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Sales consumer";
Console.WriteLine("SALES");
RunMassTransitReceiver();
}
private static void RunMassTransitReceiver()
{
IBusControl rabbitBusControl = Bus.Factory.CreateUsingRabbitMq(rabbit =>
{
rabbit.Host(new Uri("rabbitmq://localhost:5672"), settings =>
{
settings.Password("guest");
settings.Username("guest");
});
rabbit.ReceiveEndpoint("mycompany.domains.queues.events.sales", conf =>
{
conf.Consumer<CustomerRegisteredConsumerSls>();
});
});
rabbitBusControl.Start();
Console.ReadKey();
rabbitBusControl.Stop();
}
}
}
You can find a working solution on https://github.com/prasantj409/Masstransit-PublishMultipleConsumer.git
By default, RabbitMQ sends each message to all the consumers in sequence. This type of dispatching is called "round-robin" and made for load balancing (you can have multiple instances of your service consuming the same message).
As Chris pointed, to ensure that your service always receives its copy of a message, you need to provide the unique Queue name.
What you need to do:
Make sure that your consumers implements IConsumer interface with the same generic type
Register all this consumers
Use Publish method to send message
Generally there are two types of messages in MassTransit: Events and Commands, and in this case your message is Event. In the case when your message is a Command, only one consumer receives message and you need to use Send method.
Example of Event DTO:
public class OrderChecked
{
public Guid OrderId { get; set; }
}
Consumers:
public class OrderSuccessfullyCheckedConsumer : IConsumer<OrderChecked>
{
public async Task Consume(ConsumeContext<OrderChecked> context)
{
// some your consuming code
}
}
public class OrderSuccessfullyCheckedConsumer2 : IConsumer<OrderChecked>
{
public async Task Consume(ConsumeContext<OrderChecked> context)
{
// some your second consuming code
}
}
Configuring:
services.AddMassTransit(c =>
{
c.AddConsumer<OrderSuccessfullyCheckedConsumer>();
c.AddConsumer<OrderSuccessfullyCheckedConsumer2>();
c.SetKebabCaseEndpointNameFormatter();
c.UsingRabbitMq((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService(true);
Publishing the message:
var endpoint = await _bus.GetPublishSendEndpoint<OrderChecked>();
await endpoint.Send(new OrderChecked
{
OrderId = newOrder.Id
});
I want to share a slightly different code example.
instanceId:
Specifies an identifier that uniquely identifies the endpoint
instance, which is appended to the end of the endpoint name.
services.AddMassTransit(x => {
x.SetKebabCaseEndpointNameFormatter();
Guid instanceId = Guid.NewGuid();
x.AddConsumer<MyConsumer>()
.Endpoint(c => c.InstanceId = instanceId.ToString());
x.UsingRabbitMq((context, cfg) => {
...
cfg.ConfigureEndpoints(context);
});
});
Is there a small example of a console or winform app using signalR to send a message to a .net hub?. I have tried the .net examples and have looked at the wiki but it is not making sense to me the relationship between the hub(.net) and client(console app) (could not find an example of this). Does the app just need the address and name of hub to connect?.
If someone could provide a small tidbit of code showing the app connecting to a hub and sending "Hello World" or something that the .net hub receives?.
PS. I have a standard hub chat example which works well , if I try to assign a hub name in Cs to it , it stops working i.e [HubName("test")] , do you know the reason for this?.
Thanks.
Current Console app Code.
static void Main(string[] args)
{
//Set connection
var connection = new HubConnection("http://localhost:41627/");
//Make proxy to hub based on hub name on server
var myHub = connection.CreateProxy("chat");
//Start connection
connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection:{0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Connected");
}
}).Wait();
//connection.StateChanged += connection_StateChanged;
myHub.Invoke("Send", "HELLO World ").ContinueWith(task => {
if(task.IsFaulted)
{
Console.WriteLine("There was an error calling send: {0}",task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Send Complete.");
}
});
}
Hub Server. (different project workspace)
public class Chat : Hub
{
public void Send(string message)
{
// Call the addMessage method on all clients
Clients.addMessage(message);
}
}
Info Wiki for this is http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-net-client
First of all, you should install SignalR.Host.Self on the server application and SignalR.Client on your client application by nuget :
PM> Install-Package SignalR.Hosting.Self -Version 0.5.2
PM> Install-Package Microsoft.AspNet.SignalR.Client
Then add the following code to your projects ;)
(run the projects as administrator)
Server console app:
using System;
using SignalR.Hubs;
namespace SignalR.Hosting.Self.Samples {
class Program {
static void Main(string[] args) {
string url = "http://127.0.0.1:8088/";
var server = new Server(url);
// Map the default hub url (/signalr)
server.MapHubs();
// Start the server
server.Start();
Console.WriteLine("Server running on {0}", url);
// Keep going until somebody hits 'x'
while (true) {
ConsoleKeyInfo ki = Console.ReadKey(true);
if (ki.Key == ConsoleKey.X) {
break;
}
}
}
[HubName("CustomHub")]
public class MyHub : Hub {
public string Send(string message) {
return message;
}
public void DoSomething(string param) {
Clients.addMessage(param);
}
}
}
}
Client console app:
using System;
using SignalR.Client.Hubs;
namespace SignalRConsoleApp {
internal class Program {
private static void Main(string[] args) {
//Set connection
var connection = new HubConnection("http://127.0.0.1:8088/");
//Make proxy to hub based on hub name on server
var myHub = connection.CreateHubProxy("CustomHub");
//Start connection
connection.Start().ContinueWith(task => {
if (task.IsFaulted) {
Console.WriteLine("There was an error opening the connection:{0}",
task.Exception.GetBaseException());
} else {
Console.WriteLine("Connected");
}
}).Wait();
myHub.Invoke<string>("Send", "HELLO World ").ContinueWith(task => {
if (task.IsFaulted) {
Console.WriteLine("There was an error calling send: {0}",
task.Exception.GetBaseException());
} else {
Console.WriteLine(task.Result);
}
});
myHub.On<string>("addMessage", param => {
Console.WriteLine(param);
});
myHub.Invoke<string>("DoSomething", "I'm doing something!!!").Wait();
Console.Read();
connection.Stop();
}
}
}
Example for SignalR 2.2.1 (May 2017)
Server
Install-Package Microsoft.AspNet.SignalR.SelfHost -Version 2.2.1
[assembly: OwinStartup(typeof(Program.Startup))]
namespace ConsoleApplication116_SignalRServer
{
class Program
{
static IDisposable SignalR;
static void Main(string[] args)
{
string url = "http://127.0.0.1:8088";
SignalR = WebApp.Start(url);
Console.ReadKey();
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
/* CAMEL CASE & JSON DATE FORMATTING
use SignalRContractResolver from
https://stackoverflow.com/questions/30005575/signalr-use-camel-case
var settings = new JsonSerializerSettings()
{
DateFormatHandling = DateFormatHandling.IsoDateFormat,
DateTimeZoneHandling = DateTimeZoneHandling.Utc
};
settings.ContractResolver = new SignalRContractResolver();
var serializer = JsonSerializer.Create(settings);
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);
*/
app.MapSignalR();
}
}
[HubName("MyHub")]
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
}
}
}
Client
(almost the same as Mehrdad Bahrainy reply)
Install-Package Microsoft.AspNet.SignalR.Client -Version 2.2.1
namespace ConsoleApplication116_SignalRClient
{
class Program
{
private static void Main(string[] args)
{
var connection = new HubConnection("http://127.0.0.1:8088/");
var myHub = connection.CreateHubProxy("MyHub");
Console.WriteLine("Enter your name");
string name = Console.ReadLine();
connection.Start().ContinueWith(task => {
if (task.IsFaulted)
{
Console.WriteLine("There was an error opening the connection:{0}", task.Exception.GetBaseException());
}
else
{
Console.WriteLine("Connected");
myHub.On<string, string>("addMessage", (s1, s2) => {
Console.WriteLine(s1 + ": " + s2);
});
while (true)
{
Console.WriteLine("Please Enter Message");
string message = Console.ReadLine();
if (string.IsNullOrEmpty(message))
{
break;
}
myHub.Invoke<string>("Send", name, message).ContinueWith(task1 => {
if (task1.IsFaulted)
{
Console.WriteLine("There was an error calling send: {0}", task1.Exception.GetBaseException());
}
else
{
Console.WriteLine(task1.Result);
}
});
}
}
}).Wait();
Console.Read();
connection.Stop();
}
}
}
To build on #dyslexicanaboko's answer for dotnet core, here is a client console application:
Create a helper class:
using System;
using Microsoft.AspNetCore.SignalR.Client;
namespace com.stackoverflow.SignalRClientConsoleApp
{
public class SignalRConnection
{
public async void Start()
{
var url = "http://signalr-server-url/hubname";
var connection = new HubConnectionBuilder()
.WithUrl(url)
.WithAutomaticReconnect()
.Build();
// receive a message from the hub
connection.On<string, string>("ReceiveMessage", (user, message) => OnReceiveMessage(user, message));
var t = connection.StartAsync();
t.Wait();
// send a message to the hub
await connection.InvokeAsync("SendMessage", "ConsoleApp", "Message from the console app");
}
private void OnReceiveMessage(string user, string message)
{
Console.WriteLine($"{user}: {message}");
}
}
}
Then implement in your console app's entry point:
using System;
namespace com.stackoverflow.SignalRClientConsoleApp
{
class Program
{
static void Main(string[] args)
{
var signalRConnection = new SignalRConnection();
signalRConnection.Start();
Console.Read();
}
}
}
The Self-Host now uses Owin. Checkout http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-signalr-20-self-host to setup the server. It's compatible with the client code above.
This is for dot net core 2.1 - after a lot of trial and error I finally got this to work flawlessly:
var url = "Hub URL goes here";
var connection = new HubConnectionBuilder()
.WithUrl($"{url}")
.WithAutomaticReconnect() //I don't think this is totally required, but can't hurt either
.Build();
//Start the connection
var t = connection.StartAsync();
//Wait for the connection to complete
t.Wait();
//Make your call - but in this case don't wait for a response
//if your goal is to set it and forget it
await connection.InvokeAsync("SendMessage", "User-Server", "Message from the server");
This code is from your typical SignalR poor man's chat client. The problem that I and what seems like a lot of other people have run into is establishing a connection before attempting to send a message to the hub. This is critical, so it is important to wait for the asynchronous task to complete - which means we are making it synchronous by waiting for the task to complete.
When creating a NServiceBus SendOnly endpoint, the purpose is to just fire-and-forget, i.e. just send a message and then someone else will take care of it. Which seems like the thing I need. I dont want any communication between the bus and the system handling messages. System "A" wants to notify system "B" about something.
Well the creation of an SendOnly endpoint if very straightforward but what about the system listening for messages from an SendOnly endpoint.
I'm trying to set up a listener in a commandline project that will handle messages. The messages get sent to the queue but they doesnt get handled by system "B".
Is this the wrong approach? Is a bus overkill for this type of functionality?
System A:
public class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
var bus = Configure.With()
.UnityBuilder(container)
.JsonSerializer()
.Log4Net()
.MsmqTransport()
.UnicastBus()
.SendOnly();
while(true)
{
Console.WriteLine("Send a message");
var message = new Message(Console.ReadLine());
bus.Send(message);
}
}
}
System B:
class Program
{
static void Main(string[] args)
{
var container = new UnityContainer();
var bus = Configure.With()
.UnityBuilder(container)
.JsonSerializer()
.Log4Net()
.MsmqTransport()
.UnicastBus()
.LoadMessageHandlers()
.CreateBus()
.Start();
Console.WriteLine("Waiting for messages...");
while(true)
{
}
}
}
public class MessageHandler : IHandleMessages<Message>
{
public void Handle(Message message)
{
Console.WriteLine(message.Data);
}
}
public class Message : IMessage
{
public Message()
{
}
public Message(string data)
{
Data = data;
}
public string Data { get; set; }
}
In the MessageEndpointMappings you need to update it as follows:
Replace DLL with the name of the assembly containing your messages (e.g. "Messages")
Change the Endpoint to the name of the queue which System B is reading from (You can check the queue name by looking in the MSMQ snapin under private queues).
<add Messages="Messages" Endpoint="SystemB" />
NServiceBus 3 automatically creates the queue name based upon the namespace of the hosting assembly.
Additionally, you may want to look at using the NServiceBus.Host to host your handlers instead of your own console application.