MqttNet version 4.1.3.563 Basic example - c#

Following this example I have now therefore been required to update the MQTT.NET from version 3 (that works thanks the provided help) to version 4.
A very basic set of capabilities would be enough:
Connect to an adress with a timeout
Check if the connection has gone well
Receive messages
check disconnection
that was extremely easy in version 3
MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder()
.WithClientId("IoApp" + HelperN.MQTT.GetClientID(true))
.WithTcpServer("localhost", 1883);
ManagedMqttClientOptions options = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(60))
.WithClientOptions(builder.Build())
.Build();
mqttClient = new MqttFactory().CreateManagedMqttClient();
mqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(OnConnected);
mqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(OnDisconnected);
mqttClient.ConnectingFailedHandler = new ConnectingFailedHandlerDelegate(OnConnectingFailed);
mqttClient.SubscribeAsync(...);
mqttClient.SubscribeAsync(...);
mqttClient.StartAsync(options).GetAwaiter().GetResult();
mqttClient.UseApplicationMessageReceivedHandler(args => { OnMessageReceived(args); });
but when it comes to version 4 if I have to relay on those examples I have problems.
Let's start from the connection
public static async Task Connect_Client_Timeout()
{
/*
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
*
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
*/
var mqttFactory = new MqttFactory();
strError = String.Empty;
using (var mqttClient = mqttFactory.CreateMqttClient())
{
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("aaaa127.0.0.1",1883).Build();
try
{
using (var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
{
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
}
}
catch (OperationCanceledException exc)
{
strError = "Connect_Client_Timeout exc:" + exc.Message;
}
}
}
And I call this task from the main awaiting the result.
var connectTask = Connect_Client_Timeout();
connectTask.Wait();<-----never ends
Since I put a wrong address "aaaa127.0.0.1" I expect a failure after 5 seconds. But the connectTask.Wait never end. But even if I put the right address "127.0.0.1" it never exits.
So perhaps the error stands in the connectTask.Wait();.
Thanks

The solution is here
In short you have to do this:
static async Task Connect()
{
IManagedMqttClient _mqttClient = new MqttFactory().CreateManagedMqttClient();
// Create client options object
MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder()
.WithClientId("behroozbc")
.WithTcpServer("localhost");
ManagedMqttClientOptions options = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(60))
.WithClientOptions(builder.Build())
.Build();
// Set up handlers
_mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync;
_mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync;
_mqttClient.ConnectingFailedAsync += _mqttClient_ConnectingFailedAsync;
// Connect to the broker
await _mqttClient.StartAsync(options);
// Send a new message to the broker every second
while (true)
{
string json = JsonSerializer.Serialize(new { message = "Hi Mqtt", sent = DateTime.UtcNow });
await _mqttClient.EnqueueAsync("behroozbc.ir/topic/json", json);
await Task.Delay(TimeSpan.FromSeconds(1));
}
Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
{
Console.WriteLine("Connected");
return Task.CompletedTask;
};
Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
Console.WriteLine("Disconnected");
return Task.CompletedTask;
};
Task _mqttClient_ConnectingFailedAsync(ConnectingFailedEventArgs arg)
{
Console.WriteLine("Connection failed check network or broker!");
return Task.CompletedTask;
}
}
and then just call Connect() and rely on the subscribed examples

Related

Azure Service Bus: ReceiveMessagesAsync returns only a subset

I wrote a code to read 1000 messages one shot from an Azure Service Bus queue. I read the messages with the line: await receiver.ReceiveMessagesAsync(1000); but only a subset of the messages are received.
I took the code from the sample: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/tests/Samples/Sample01_HelloWorld.cs, the SendAndReceiveMessageSafeBatch() method
This is my code:
public class Program
{
static void Main(string[] args)
{
SendAndReceiveMessage().GetAwaiter().GetResult();
}
public static async Task SendAndReceiveMessage()
{
var connectionString = "myconnectionstring";
var queueName = "myqueue";
// since ServiceBusClient implements IAsyncDisposable we create it with "await using"
await using var client = new ServiceBusClient(connectionString);
// create the sender
var sender = client.CreateSender(queueName);
IList<ServiceBusMessage> messages = new List<ServiceBusMessage>();
for (var i = 0; i < 1000; i++)
{
messages.Add(new ServiceBusMessage($"Message {i}"));
}
// send the messages
await sender.SendMessagesAsync(messages);
// create a receiver that we can use to receive the messages
var options = new ServiceBusReceiverOptions()
{
ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete
};
ServiceBusReceiver receiver = client.CreateReceiver(queueName, options);
// the received message is a different type as it contains some service set properties
IReadOnlyList<ServiceBusReceivedMessage> receivedMessages = await receiver.ReceiveMessagesAsync(1000);
Console.WriteLine($"Received {receivedMessages.Count} from the queue {queueName}");
foreach (ServiceBusReceivedMessage receivedMessage in receivedMessages)
{
var body = receivedMessage.Body.ToString();
Console.WriteLine(body);
}
Console.WriteLine("END");
Console.ReadLine();
}
}
Do you have any suggestion how to read all 1000 messages one shot?
This is expected behaviour with Azure Service Bus. The number of messages to receive, maxMessages is a maximum number that is not guaranteed.

RabbitMQ response is been lost in controller

Good evening everyone, I've got a web app written using .NET and a mobile app.
I'm sending some values to rabbitMQ server through my web app and this is working fine, i put it in a queue but when the mobile app accepts the request, i don't get the returned value.
Here is my controller
public async Task<ActionResult> GetCollect(int id)
{
int PartnerId = 0;
bool SentRequest = false;
try
{
SentRequest = await RuleRabbitMQ.SentRequestRule(id);
if(SentRequest )
{
PartnerId = await RuleRabbitMQ.RequestAccepted();
}
}
catch (Exception Ex)
{
}
}
This is my RabbitMQ class
public class InteractionRabbitMQ
{
public async Task<bool> SentRequestRule(int id)
{
bool ConnectionRabbitMQ = false;
await Task.Run(() =>
{
try
{
ConnectionFactory connectionFactory = new ConnectionFactory()
{
//credentials go here
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.QueueDeclare("SolicitacaoSameDay", true, false, false, null);
string rpcResponseQueue = channel.QueueDeclare().QueueName;
string correlationId = Guid.NewGuid().ToString();
IBasicProperties basicProperties = channel.CreateBasicProperties();
basicProperties.ReplyTo = rpcResponseQueue;
basicProperties.CorrelationId = correlationId;
byte[] messageBytes = Encoding.UTF8.GetBytes(string.Concat(" ", id.ToString()));
channel.BasicPublish("", "SolicitacaoSameDay", basicProperties, messageBytes);
channel.Close();
connection.Close();
if (connection != null)
{
ConnectionRabbitMQ = true;
}
else
{
ConnectionRabbitMQ = false;
}
}
catch (Exception Ex)
{
throw new ArgumentException($"Thre was a problem with RabbitMQ server. " +
$"Pleaser, contact the support with Error: {Ex.ToString()}");
}
});
return ConnectionRabbitMQ;
}
public async Task<int> RequestAccepted()
{
bool SearchingPartner= true;
int PartnerId = 0;
await Task.Run(() =>
{
try
{
var connectionFactory = new ConnectionFactory()
{
// credentials
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.BasicQos(0, 1, false);
var eventingBasicConsumer = new EventingBasicConsumer(channel);
eventingBasicConsumer.Received += (sender, basicDeliveryEventArgs) =>
{
string Response = Encoding.UTF8.GetString(basicDeliveryEventArgs.Body, 0, basicDeliveryEventArgs.Body.Length);
channel.BasicAck(basicDeliveryEventArgs.DeliveryTag, false);
if(!string.IsNullOrWhiteSpace(Response))
{
int Id = Convert.ToInt32(Response);
PartnerId = Id > 0 ? Id : 0;
SearchingPartner = false;
}
};
channel.BasicConsume("SolicitacaoAceitaSameDay", false, eventingBasicConsumer);
}
catch (Exception Ex)
{
// error message
}
});
return PartnerId ;
}
I am not sure this works, can't build an infrastructure to test this quickly, but - your issue is that the RequestAccepted returns a Task which completes before the Received event is caught by the Rabbit client library.
Syncing the two could possibly resolve the issue, note however that this could potentially make your code waiting very long for (or even - never get) the response.
public Task<int> RequestAccepted()
{
bool SearchingPartner= true;
int PartnerId = 0;
var connectionFactory = new ConnectionFactory()
{
// credentials
};
IConnection connection = connectionFactory.CreateConnection();
IModel channel = connection.CreateModel();
channel.BasicQos(0, 1, false);
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
var eventingBasicConsumer = new EventingBasicConsumer(channel);
eventingBasicConsumer.Received += (sender, basicDeliveryEventArgs) =>
{
string Response = Encoding.UTF8.GetString(basicDeliveryEventArgs.Body, 0, basicDeliveryEventArgs.Body.Length);
channel.BasicAck(basicDeliveryEventArgs.DeliveryTag, false);
if(!string.IsNullOrWhiteSpace(Response))
{
int Id = Convert.ToInt32(Response);
PartnerId = Id > 0 ? Id : 0;
SearchingPartner = false;
tcs.SetResult( PartnerId );
}
};
channel.BasicConsume("SolicitacaoAceitaSameDay", false, eventingBasicConsumer);
return tcs.Task;
}
There are couple of issues with this approach.
First, no error handling.
Then, what if the event is sent by the RMQ before the consumer subscribes to it? The consumer will block as it will never receive anything back.
And last, I don't think RMQ consumers are ever intended to be created in every request to your controller and then never disposed. While this could work on your dev box where you create a couple of requests manually, it won't probably ever scale to fix a scenario where dozens/hundreds of concurrent users hit your website and multiple RMQ consumers compete one against the other.
I don't think there is an easy way around it other than completely separate the consumer out of your web app, put it in a System Service or a Hangfire job and let it get responses to all possible requests and from the cache - serve responses to web requests.
This is a pure speculation, though, based on my understanding of what you try to do. I could be wrong here, of course.
byte[] messageBytes = Encoding.UTF8.GetBytes(string.Concat(" ", idColeta.ToString()));
I reckon 'idColeta' is blank.

How to check if connection to Elasticsearch is established?

I want to check if the connection to Elasticsearch database is ok. In other words I want to ping Elasticsearch. When I execute the code below, an exception is thrown.
public async Task<HealthCheckResult> Execute()
{
if (_configuration.Nodes?.Length == 0)
{
await Task.Delay(1);
return new HealthCheckResult("Connection Failed - Missing elasticsearch connection string")
{
ChildResults = new List<HealthCheckResult>() {new HealthCheckResult()}
};
}
var node = new Uri(_configuration.Nodes.First());
try
{
var connectionPool = new SniffingConnectionPool(new[] {node});
var settings = new ConnectionConfiguration(connectionPool);
var client = new ElasticLowLevelClient(settings);
client.IndicesExists<string>("applications");
}
catch (Exception exception)
{
return new HealthCheckResult(exception.Message)
{
ChildResults = new List<HealthCheckResult>() { new HealthCheckResult() }
};
}
return new HealthCheckResult("Connection Passed")
{
ChildResults = new List<HealthCheckResult>() { new HealthCheckResult() }
};
}
When I execute method above, exception is thrown and I get this message:
Failed sniffing cluster state.
What can I do to check if the connection to Elasticsearch is established?
The Nest IElasticClient interface provides a Ping method for this purpose
I was having the same problem and I managed to fix this by changing the SniffingConnectionPool to a SingleNodeConnectionPool.

Azure Service Bus Not Detecting Duplicates

I have a process that reads a message from an Azure Service Bus Queue and converts that message to a Video to be Encoded by Azure Media Services. I noticed that if the process is kicked off very quickly in a row, the same video was being encoded right after another. Here is my code that adds the Video to the Queue
public class VideoManager
{
string _connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
string _queueName = ConfigurationManager.AppSettings["ServiceBusQueueName"];
QueueClient _client;
public VideoManager()
{
var conStringBuilder = new ServiceBusConnectionStringBuilder(_connectionString)
{
OperationTimeout = TimeSpan.FromMinutes(120)
};
var messagingFactory = MessagingFactory.CreateFromConnectionString(conStringBuilder.ToString());
_client = messagingFactory.CreateQueueClient(_queueName);
}
public void Approve(Video video)
{
// Set video to approved.
video.ApprovalStatus = ApprovalStatus.Approved;
var message = new BrokeredMessage(new VideoMessage(video, VideoMessage.MessageTypes.Approve, string.Empty));
message.MessageId = video.RowKey;
_client.Send(message);
}
}
And the process that reads from the Queue
class Program
{
static QueueClient client;
static void Main(string[] args)
{
VideoManager videoManager = new VideoManager();
var connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"];
var conStringBuilder = new ServiceBusConnectionStringBuilder(connectionString)
{
OperationTimeout = TimeSpan.FromMinutes(120)
};
var messagingFactory = MessagingFactory.CreateFromConnectionString(conStringBuilder.ToString());
client = messagingFactory.CreateQueueClient(ConfigurationManager.AppSettings["ServiceBusQueueName"]);
Console.WriteLine("Starting: Broadcast Center Continuous Video Processing Job");
OnMessageOptions options = new OnMessageOptions
{
MaxConcurrentCalls = 25,
AutoComplete = false
};
client.OnMessageAsync(async message =>
{
bool shouldAbandon = false;
try
{
await HandleMessage(message);
}
catch (Exception ex)
{
shouldAbandon = true;
Console.WriteLine(ex.Message);
}
if (shouldAbandon)
{
await message.AbandonAsync();
}
}, options);
while (true) { }
}
async static Task<int> HandleMessage(BrokeredMessage message)
{
VideoMessage videoMessage = message.GetBody<VideoMessage>();
Console.WriteLine(String.Format("Message body: {0}", videoMessage.Video.Title));
Console.WriteLine(String.Format("Message id: {0}", message.MessageId));
VideoProcessingService vp = new VideoProcessingService(videoMessage.Video);
Task task;
switch (videoMessage.MessageType)
{
case VideoMessage.MessageTypes.CreateThumbnail:
task = new Task(() => vp.ProcessThumbnail(videoMessage.TimeStamp));
task.Start();
while (!task.IsCompleted)
{
await Task.Delay(15000);
message.RenewLock();
}
await task;
Console.WriteLine(task.Status.ToString());
Console.WriteLine("Processing Complete");
Console.WriteLine("Awaiting Message");
break;
case VideoMessage.MessageTypes.Approve:
task = new Task(() => vp.Approve());
task.Start();
while (!task.IsCompleted)
{
await Task.Delay(15000);
message.RenewLock();
}
await task;
Console.WriteLine(task.Status.ToString());
Console.WriteLine("Processing Complete");
Console.WriteLine("Awaiting Message");
break;
default:
break;
}
return 0;
}
}
What I see in the Console Window is the following if I kick off the process 3 times in a row
Message id: 76aca19a-0698-449b-bf58-a24876fc4314
Message id: 76aca19a-0698-449b-bf58-a24876fc4314
Message id: 76aca19a-0698-449b-bf58-a24876fc4314
I thought maybe I did not have the settings correct, but they are there
I am really at a loss here, as I would expect this to be fairly out of the box behavior. Does duplicate detection only work if the message has been completed, so I can't use OnMessageAsync()?
The issue is not the completion (as it was in the code), but the fact that you have in essence multiple consumers (25 concurrent callbacks) and it seems like the LockDuration is elapsing faster than the processing takes. As a result of that, message re-appears and re-processed. As a result of that you see the same message ID logged more than once.
Possible solutions are (as I've outlined in a comment above):
Let OnMessage API manage timeout extension for you (example)
Manually renew the lock as you've done using BrokeredMessage.RenewLock
There is a line of code missing from your HandleMessage code.
async static Task<int> HandleMessage(BrokeredMessage message)
{
VideoMessage videoMessage = message.GetBody<VideoMessage>();
message.CompleteAsync(); // This line...
Console.WriteLine(String.Format("Message id: {0}", message.MessageId));
// Processes Message
}
So yes you have to mark the message with either, Complete, Defer etc..
Also see this Answer, also found this which may be useful in how duplicate detection works

SSDP (UDP) on Windows Store applications (.NET)

I am trying to implement a basic SSDP (UDP) broadcast/listener for a Windows Store application using C#.
I have found that Windows.Networking.Sockets contains the DatagramSocket class which is what I need to use for UDP networking.
However, my current attempts seem to execute just fine but have no results via Wireshark and do not get a response back from the devices on the network.
Here is the code I am currently using (and running through the RT Simulator):
public async static Task<IEnumerable<HueBridge>> DiscoverAsync(TimeSpan timeout)
{
if (timeout <= TimeSpan.Zero)
throw new ArgumentException("Timeout value must be greater than zero.", "timeout");
var discoveredBridges = new List<HueBridge>();
using (var socket = new DatagramSocket())
{
while (true)
{
var bridgeWasFound = false;
socket.MessageReceived += (sender, e) =>
{
var bpx = true; // breakpoint here for success
};
var multicastIP = new HostName("239.255.255.250");
await socket.BindServiceNameAsync("1900");
socket.JoinMulticastGroup(multicastIP);
using (var writer = new DataWriter(socket.OutputStream))
{
var request = new StringBuilder();
request.AppendLine("M-SEARCH * HTTP/1.1");
request.AppendLine("HOST: 239.255.255.250:1900");
request.AppendLine("MAN: ssdp:discover");
request.AppendLine("MX: 5");
request.AppendLine("ST: ssdp:all");
writer.WriteString(request.ToString());
await writer.FlushAsync();
}
if (timeout > TimeSpan.Zero)
await Task.Delay(timeout);
if (!bridgeWasFound)
break; // breakpoint here for failure check
}
}
return discoveredBridges;
}
Any ideas on what I may be doing incorrectly? I don't get an exception and I have the proper Capabilities set in the manifest. My breakpoint at the break always gets hit and I am using a timeout of 10 seconds.
Seems I have found the problem(s).
First, I should use socket.BindEndpointAsync(null, string.Empty) instead of socket.BindServiceNameAsync("1900"), which will properly listen for broadcast packets.
Secondly, writer.FlushAsync() does not write to the socket; however, writer.StoreAsync() does.
Here is the final result, which does work (almost) perfectly:
public async static Task<IEnumerable<HueBridge>> DiscoverAsync(TimeSpan timeout)
{
if (timeout <= TimeSpan.Zero)
throw new ArgumentException("Timeout value must be greater than zero.", "timeout");
var discoveredBridges = new List<HueBridge>();
var multicastIP = new HostName("239.255.255.250");
var bridgeWasFound = false;
using (var socket = new DatagramSocket())
{
socket.MessageReceived += (sender, e) =>
{
var reader = e.GetDataReader();
var bytesRemaining = reader.UnconsumedBufferLength;
var receivedString = reader.ReadString(bytesRemaining);
// TODO: Check for existing bridges, only add new ones to prevent infinite loop.
// TODO: Create new bridge and add to the list.
bridgeWasFound = true;
};
await socket.BindEndpointAsync(null, string.Empty);
socket.JoinMulticastGroup(multicastIP);
while (true)
{
bridgeWasFound = false;
using (var stream = await socket.GetOutputStreamAsync(multicastIP, "1900"))
using (var writer = new DataWriter(stream))
{
var request = new StringBuilder();
request.AppendLine("M-SEARCH * HTTP/1.1");
request.AppendLine("HOST: 239.255.255.250:1900");
request.AppendLine("MAN: ssdp:discover");
request.AppendLine("MX: 3");
request.AppendLine("ST: ssdp:all");
writer.WriteString(request.ToString());
await writer.StoreAsync();
if (timeout > TimeSpan.Zero)
await Task.Delay(timeout);
if (!bridgeWasFound)
break;
}
}
}
return discoveredBridges;
}
According Specifications :
MAN REQUIRED by HTTP Extension Framework. Unlike the NTS and ST field
values, the field value of the MAN header field is enclosed in double
quotes; it defines the scope (namespace) of the extension. MUST be
"ssdp:discover".
then your code
request.AppendLine("MAN: ssdp:discover");
must be
request.AppendLine("MAN: \"ssdp:discover\"");
Hope this help.

Categories

Resources