RabbitMQ: Sending messages from one pc to another - c#

I am trying to get a rabbitMQ queue set up that sits on one pc, and recieves messagess from other computers giving it tasks. I have followed all the tutorials on the rabbit website but these only apply to local host. Can someone explain how I get this same code to communicate across 2 computers, not just from the same computer.
I have the following code:
Sender.cs
class Send
{
static void Main(string[] args)
{
Console.WriteLine("------------------");
Console.WriteLine("RabbitMQ Test");
Console.WriteLine("------------------");
var factory = new ConnectionFactory() { HostName = "localHost" };
try
{
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("abc", false, false, false, null);
Console.WriteLine("Enter the messages you want to send (Type 'exit' to close program)...");
string message = null;
while (message != "exit")
{
message = Console.ReadLine();
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish("", "abc", null, body);
}
}
}
}
catch (Exception e)
{
string message = e.ToString();
}
}
Reciever.cs
class Recieve
{
static void Main(string[] args)
{
ConnectionFactory factory = new ConnectionFactory()
{
HostName = "localhost"
};
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
channel.QueueDeclare("abc", false, false, false, null);
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("abc", true, consumer);
Console.WriteLine(" [*] Waiting for messages." +
"To exit press CTRL+C");
while (true)
{
var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine("[Recieved]: {0}", message);
}
}
}
}
}
Is the idea to get these communicating across 2 computers to change the ConnectionFactory's hostname to the IP of the other computer or something to that extent? I have installed rabbit correctly on both computers, and this code runs correctly on each computer individually. I just need the communication to happen across the computers.
Any help would be greatly appreciated. I can't find any examples of this anywhere on the internet.

RabbitMQ is a centralized message queue. You would only install it on your server machine (or a cluster of server machines), and not on each publishing or subscribing client. Clients would then subscribe or publish to queues on the centralized machine.
In your development environment you should decide which of your two machines you want to act as the server. Then pass that hostname and password each client. Both clients should be working towards the same hostname.
var server = "http://127.0.0.1"; // An install of RabbitMQ reachable from both clients
var password = "guest";
var username = "guest";
var connectionFactory = new ConnectionFactory { HostName = server, Password = password , Username = username};
If you want to do message passing without installing something on a server you should take a look at ZeroMQ

You can check out shovel plugin - it is built to take messages messages from queue on one node of rabbit and publishes these messages on other one, while taking care of poor networking (WAN).
Check the official description

Related

How to avoid having to pass a "root certificate" to the client when developing a gRpc service between 2 desktop applications

I've 2 desktop applications in .Net that communicate between each other. Up until now they were using WCF but we are moving toward .Net 6 so we have to update them.
They run on a local network without internet connection but we require the user to enter a password, just to avoid the technician to connect to the wrong server and misconfigure something.
Our server is a WPF application, so we will not be hosting the service inside an Asp.Net server.
Currently, I've made and retrieved this code:
[Test]
public void CustomTest()
{
string rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath);
keyCertPair = new KeyCertificatePair(
File.ReadAllText(TestCredentials.ServerCertChainPath),
File.ReadAllText(TestCredentials.ServerPrivateKeyPath));
VerifyPeerCallback verifyFunc = context =>
{
return true;
};
var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, SslClientCertificateRequestType.DontRequest);
var clientCredentials = new SslCredentials(rootCert, null, verifyFunc);
// Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
{
Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
};
sslCredentialsTestServiceImpl = new SslCredentialsTestServiceImpl();
server.Services.AddCodeFirst<ITestService>(sslCredentialsTestServiceImpl, default, Console.Out);
server.Start();
var options = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
client = channel.CreateGrpcService<ITestService>();
CallContext callContext = default;
var call = client.UnaryCall(new SimpleRequest(), callContext);
Assert.AreEqual(false, sslCredentialsTestServiceImpl.WasPeerAuthenticated);
}
It works. But:
I don't understand why we have to provide the root certificate to the client? Shouldn't this check the windows certificate directory for the available certificate?
Same question for the server. I understand why we have to provide the certificate, but not why we need to provide the CA certificate?
Also, is there an (unsecure) way to use credentials but no SSL? I know that make no sense in a security standpoint, but in our case, it's just to avoid dum errors, the user will either be on the same computer with the 2 software, either in the same room

gRPC client call fails when changing localhost to the server computer name or IP

I have a gRPC client calling into a server on the same machine and client code uses the channel created with:
Channel channel = new Channel("localhost", 11112, ChannelCredentials.Insecure);
This works well, and I'm getting the reply as expected.
However, if I replace "localhost" with the actual machine name (FQDN) or IP address, I'm getting an error:
Status(StatusCode="Unavailable", Detail="failed to connect to all addresses", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"#1616190440.553000000","description":"Failed to pick subchannel","file":"..\..\..\src\core\ext\filters\client_channel\client_channel.cc","file_line":5397,"referenced_errors":[{"created":"#1616190440.553000000","description":"failed to connect to all addresses","file":"..\..\..\src\core\ext\filters\client_channel\lb_policy\pick_first\pick_first.cc","file_line":398,"grpc_status":14}]}")
I also tested starting the server on a remote machine and having the client call into that remote machine (by name or IP) when creating the channel, but got the same failure.
The application is written in C# .NET Core 3.2 as a Console application. Nuget packages included are: Google.Protobuf, Grpc.Core, Grpc.Tools.
Not sure what is wrong in my code or whether something is not setup properly on my machine.
Here's the reset of the relevant client code:
MWCS.McpsServer.McpsServerClient client = new MWCS.McpsServer.McpsServerClient(channel);
var data = new ProcessResponseInput() { ClientID = clientID };
var options = new CallOptions();
try {
var res = client.ProcessResponse(data, options);
return res;
} catch (RpcException ex) {
StandardOutput so = new StandardOutput() { ExitCode = 1, Message = ex.Message };
return so;
}
To resolve this, I actually had to fix the server code. I should have used "0.0.0.0" instead of "localhost" in the bind address on the server. Looks like when setting the server to localhost it is not visible outside the computer.
Here's how I have the server started now (after the replacement):
_server = new Server() {
Services = { McpsServer.BindService(new McpcServerImplementation()) },
Ports = { new ServerPort("0.0.0.0", 11112, ServerCredentials.Insecure) }
};

Email sending with SendGrid using WebJobs

I'm trying to integrate SendGrid to a .Net 4.5 application using WebJobs.
I made the basic configurations required to send a basic email. I'm trying to run and test it in my local machine. I can't figure out how to push messages to the queue. I can't upgrade the .Net version of the application as of now. If it is possible to do this without using webjobs that is also fine.
Program.cs
static void Main()
{
var config = new JobHostConfiguration();
config.UseTimers();
config.Queues.MaxDequeueCount = 2;
config.Queues.MaxPollingInterval = TimeSpan.FromSeconds(4);
config.Queues.BatchSize = 2;
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
config.UseSendGrid();
var host = new JobHost(config);
host.RunAndBlock();
}
Functions.cs
public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [SendGrid(From = "no-reply#company.com", To = "employee#company.com")] out Mail mail)
{
log.WriteLine(message);
mail = new Mail();
var personalization = new Personalization();
personalization.AddBcc(new Email("employee#company.com"));
mail.AddPersonalization(personalization);
mail.Subject = "Test Email Subject";
mail.AddContent(new Content("text/html", $"The message '{message}' was successfully processed."));
}
Found the following functions:
SendGrid_Test_002.Functions.ProcessQueueMessage
ServicePointManager.DefaultConnectionLimit is set to the default value of 2. This can limit the connection throughput to services like Azure Storage. For more information, see https://aka.ms/webjobs-connections.
Job host started
I get this on the console.
Thanks in advance :)
I just had to feed the messages into the webjob queue using the following code.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
// Retrieve a reference to a container.
CloudQueue queue = queueClient.GetQueueReference("email-queue-name");
// Create the queue if it doesn't already exist
queue.CreateIfNotExists();
// Create message to be sent to the queue
CloudQueueMessage message = new CloudQueueMessage(JsonConvert.SerializeObject(new
{
From = emailContent.From,
To = emailContent.To,
Cc = emailContent.Cc,
Bcc = emailContent.Bcc,
Subject = emailContent.Subject,
Message = emailContent.Message
}).ToString());
queue.AddMessage(message);

ServiceBus throws 401 Unauthorized Error

I'm using a simple implementation of the Windows Service Bus 1.0 Brokered messaging to keep track of the user interactions with a particular web application.
Every time something is saved to a "sensitive" table in the database, I have setup the repository layer send a message like so:
ServiceBus.MessageQueue<T>.PushAsync(entity);
which will then serialize the entity and create a message out of it.
My MessageQueue class is something like this.
public static class MessageQueue<T>
{
static string ServerFQDN;
static int HttpPort = 9355;
static int TcpPort = 9354;
static string ServiceNamespace = "ServiceBusDefaultNamespace";
public static void PushAsync(T msg)
{
ServerFQDN = System.Net.Dns.GetHostEntry(string.Empty).HostName;
//Service Bus connection string
var connBuilder = new ServiceBusConnectionStringBuilder { ManagementPort = HttpPort, RuntimePort = TcpPort };
connBuilder.Endpoints.Add(new UriBuilder() { Scheme = "sb", Host = ServerFQDN, Path = ServiceNamespace }.Uri);
connBuilder.StsEndpoints.Add(new UriBuilder() { Scheme = "https", Host = ServerFQDN, Port = HttpPort, Path = ServiceNamespace}.Uri);
//Create a NamespaceManager instance (for management operations) and a MessagingFactory instance (for sending and receiving messages)
MessagingFactory messageFactory = MessagingFactory.CreateFromConnectionString(connBuilder.ToString());
NamespaceManager namespaceManager = NamespaceManager.CreateFromConnectionString(connBuilder.ToString());
if (namespaceManager == null)
{
Console.WriteLine("\nUnexpected Error");
return;
}
//create a new queue
string QueueName = "ServiceBusQueueSample";
if (!namespaceManager.QueueExists(QueueName))
{
namespaceManager.CreateQueue(QueueName);
}
try
{
QueueClient myQueueClient = messageFactory.CreateQueueClient(QueueName);
string aaa = JsonConvert.SerializeObject(msg, Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new NHibernateContractResolver()
});
BrokeredMessage sendMessage1 = new BrokeredMessage(aaa);
sendMessage1.Properties.Add("UserName",Thread.CurrentPrincipal.Identity.Name);
sendMessage1.Properties.Add("TimeStamp", ApplicationDateTime.Now);
sendMessage1.Properties.Add("Type", msg.GetType().Name);
myQueueClient.Send(sendMessage1);
}
catch (Exception e)
{
var l = new Logger();
l.Log(LogEventEnum.WebrequestFailure, e.Message);
Console.WriteLine("Unexpected exception {0}", e.ToString());
throw;
}
}
}
This works flawlessly when I debug this locally. But when I publish the site in IIS and run, the namespaceManager.QueueExists(QueueName) call fails with an exception which says "401 Unauthorized error".
When I change the Application pool identity (in IIS) to an admin account this error does not occur. However, there is absolutely no way that I can make this happen when we go production.
Am I missing something? If so, what is it? Any help is greatly appreciated.
Did you read the security section in the docs, Chameera? http://msdn.microsoft.com/en-us/library/windowsazure/jj193003(v=azure.10).aspx
You seem to be running with the default security settings, meaning you only have admin accounts authorized. Review the documentation section and grant the requisite rights to the accounts you want to use in prod.

HTTPListener not working over network

I tried creating a simple HTTP server using System.Net.HTTPListener, but it doesn't receive connections from other computers in the network. Example code:
class HTTPServer
{
private HttpListener listener;
public HTTPServer() { }
public bool Start()
{
listener = new HttpListener();
listener.Prefixes.Add("http://+:80/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
return true;
}
private static void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
Console.WriteLine("New request.");
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
byte[] page = Encoding.UTF8.GetBytes("Test");
response.ContentLength64 = page.Length;
Stream output = response.OutputStream;
output.Write(page, 0, page.Length);
output.Close();
}
}
class Program
{
static void Main(string[] args)
{
HTTPServer test = new HTTPServer();
test.Start();
while (true) ;
}
}
Is there something wrong with this code, or is there another problem?
I've tried running the application with administrator privileges, but when I browse to the computer's IP address (i.e. 192.168.1.100) on another computer, I never receive the request. The server works fine if the request is sent from the same computer as where the application is running (using "localhost", "127.0.0.1" and "192.168.1.100"). Pinging works fine. I've also tried nginx, and that works perfectly over the network.
I'm using HTTPListener as a lightweight server to deliver a webpage with a Silverlight XAP file with some dynamic init params, clientaccesspolicy.xml and a simple mobile HTML page.
Firewall
I also thought first of the Firewall. However the problem where my endpoints:
From a tutorial I had a code like the following
String[] endpoints = new String[] {
"http://localhost:8080/do_something/",
// ...
};
This code only works locally and only if you use localhost. To be able to use the IP, I changed it to
String[] endpoints = new String[] {
"http://127.0.0.1:8080/do_something/",
// ...
};
This time the request by ip adress worked, but the server did not respond to remote requests from another ip. What got me working for me was to use a star (*) instead of localhost and 127.0.0.1, so the following code:
String[] endpoints = new String[] {
"http://*:8080/do_something/",
// ...
};
Just leaving this here if somebody stumbles upon this post as I did.

Categories

Resources