I have a Message Publishing Server class which creates a PUB socket at construction, with the following code:
this.context = NetMQContext.Create();
this.pubSocket = this.context.CreatePublisherSocket();
var portNumber = this.installerSettings.PublisherPort;
this.pubSocket.Bind("tcp://127.0.0.1:" + portNumber);
Sending a message using messagePublishingServer.Publish(message) executes:
this.pubSocket.SendMoreFrame(string.Empty).SendFrame(message);
The following xBehave test...
[Scenario]
public void PublishMessageScenario()
{
MessagePublishingServer messagePublishingServer = null;
NetMQContext context;
NetMQ.Sockets.SubscriberSocket subSocket = null;
string receivedMessage = null;
"Given a running message publishing server"._(() =>
{
var installerSettingsManager = A.Fake<IInstallerSettingsManager>();
var settings = new InstallerSettings { PublisherPort = "5348" };
A.CallTo(() => installerSettingsManager.Settings).Returns(settings);
messagePublishingServer = new MessagePublishingServer(installerSettingsManager);
});
"And a subscriber connected to the publishing server"._(() =>
{
context = NetMQContext.Create();
subSocket = context.CreateSubscriberSocket();
subSocket.Options.ReceiveHighWatermark = 1000;
subSocket.Connect("tcp://127.0.0.1:5348");
subSocket.Subscribe(string.Empty);
});
"When publishing a message"._(() =>
{
messagePublishingServer.Publish("test message");
// Receive the topic
subSocket.ReceiveFrameString();
// and the message
receivedMessage = subSocket.ReceiveFrameString();
});
"Then the subscriber must have received it"._(() =>
{
receivedMessage.Should().NotBeNullOrEmpty();
receivedMessage.Should().Be("test message");
});
}
... blocks in the first subSocket.ReceiveFrameString() which I find unexpected. Shouldn't the subscriber socket have queued the published message until the receive is called?
Publisher is like radio, if you was not connected and subscribed when the publisher published you miss the message. My tip is to put 100ms sleep after subscriber connect (only for testing).
From the source (ReceivingSocketExtensions.cs
):
/// Receive a single frame from socket, blocking until one arrives, and decode as a string using ...
public static string ReceiveFrameString([NotNull] this IReceivingSocket socket)
and
/// If no message is immediately available, return <c>false</c>.
public static bool TryReceiveFrameString([NotNull] this IReceivingSocket socket, out string frameString)
Related
Below is the code responsible for creating the connection:
IConnection connectionMQ;
factoryFactory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ);
IConnectionFactory cf = factoryFactory.CreateConnectionFactory();
cf.SetStringProperty(XMSC.WMQ_HOST_NAME, "host");
cf.SetIntProperty(XMSC.WMQ_PORT, port);
cf.SetStringProperty(XMSC.WMQ_CHANNEL, "channel");
cf.SetIntProperty(XMSC.WMQ_CONNECTION_MODE, XMSC.WMQ_CM_CLIENT);
cf.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, "queueManager");
cf.SetStringProperty(XMSC.WMQ_SSL_PEER_NAME, "sslPeerName");
cf.SetStringProperty(XMSC.WMQ_SSL_CIPHER_SPEC, "TLS_RSA_WITH_AES_256_CBC_SHA256");
cf.SetStringProperty(XMSC.WMQ_CCSID, "ccSid");
cf.SetStringProperty(XMSC.WMQ_SSL_KEY_REPOSITORY, "*USER");
cf.SetStringProperty(XMSC.WMQ_SSL_CLIENT_CERT_LABEL, "clientCertLabel");
connectionMQ = cf.CreateConnection();
Consumer :
using (sessionWMQ = connectionWMQ.CreateSession(true, AcknowledgeMode.AutoAcknowledge))
{
var destination = sessionWMQ.CreateQueue("queueName");
consumerAsync = sessionWMQ.CreateConsumer(destination);
var messageListener = new MessageListener(OnMessageCallback);
consumerAsync.MessageListener = messageListener;
connectionWMQ.Start();
}
OnMessageCallback:
public void OnMessageCallback(IMessage message)
{
if (message != null)
{
numMessage++;
sessionWMQ.Commit();
}
}
This implementation causes me to download exactly one message and not the next ones.
What should I change to download all messages and even if the queue is empty it will wait for the message?
I will add that there is no error and this message is correctly taken from the queue and the next time I connect this message is gone.
I'm using mqtt.net (https://github.com/chkr1011/MQTTnet) and have written a small class to handle my mqtt client. The client connects to the broker and publishes it's messages successfully. Now I want the client to also react on some topics I subscribe the client to. But this does not seem to work. I do not get any breakpoint hit. This are the relevant parts of my code:
public async Task StartAsync(CancellationToken cancellationToken)
{
//Building the mqtt config
var options = new MqttClientOptionsBuilder()
.WithTcpServer(MqttConfig.Server, MqttConfig.Port)
.WithClientId("HeaterService")
.WithCredentials(MqttConfig.User, MqttConfig.Password)
.WithTls(tlsParameters =>
{
tlsParameters.AllowUntrustedCertificates = true;
})
.WithCleanSession()
.Build();
//Getting an mqtt Instance
MqttClient = new MqttFactory().CreateMqttClient();
//Wiring up all the events...
MqttClient.UseApplicationMessageReceivedHandler( e => { HandleMessageReceived(e.ApplicationMessage); });
MqttClient.UseConnectedHandler(/*async*/ e =>
{
Console.WriteLine("### CONNECTED WITH BROKER ###");
});
await MqttClient.ConnectAsync(options);
}
The client connects successfully to the server and is possible to publish messages.
This is my messagehandler function:
private void HandleMessageReceived(MqttApplicationMessage applicationMessage)
{
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###");
Console.WriteLine($"+ Topic = {applicationMessage.Topic}");
Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(applicationMessage.Payload)}");
Console.WriteLine($"+ QoS = {applicationMessage.QualityOfServiceLevel}");
Console.WriteLine($"+ Retain = {applicationMessage.Retain}");
Console.WriteLine();
}
This is my subscribe code:
public async Task SubscribeTopic(string topic)
{
var subscribeResult = await MqttClient.SubscribeAsync(new TopicFilterBuilder()
.WithTopic(topic)
//.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
Console.WriteLine("### SUBSCRIBED ###");
Console.WriteLine("### Result: " + subscribeResult.Items.FirstOrDefault()?.ResultCode);
Console.WriteLine("### Result: " + subscribeResult.Items.FirstOrDefault()?.TopicFilter);
}
I call this function of my class with "Home/Heater/control/*";
When I use mqtt-explorer to send a test message to the topic "Home/Heater/control/test"
the functionhandler HandleMessageReceived is never hit.
What I am doing wrong?
It seems like you currently don't call your SubscribeTopic(string topic) method in your StartAsync(CancellationToken cancellationToken) method.
Besides that i personally also ran into a few problems when starting out with MQTTNet.
Like #Linuxx said pay attention to the order in wich you call subscribe and connect.
I also recommend adding a Disconnected_Handler to your client to make sure the connection doesn't terminate without your knowing.
It was important to put the subscribe after the connect for me, else I was having the same problem as you.
objClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(ReceivedMessage);
string[] strTopics = { "test/log", "test/log2" };
MqttClientSubscribeOptions objSubOptions = new MqttClientSubscribeOptions();
List<TopicFilter> objTopics = new List<TopicFilter>();
foreach(string strTopic in strTopics)
{
TopicFilter objAdd = new TopicFilter();
objAdd.Topic = strTopic;
objTopics.Add(objAdd);
}
objSubOptions.TopicFilters = objTopics;
objClient.ConnectAsync(objOptions, CancellationToken.None).Wait();
objClient.SubscribeAsync(objSubOptions); //!!!!subscribe goes here!!!!
Following the MS documentation it was not difficult to receive message(s) from subscription. However, if I'd like my application to receive a message every time new message is posted - a constant polling. Hence the OnMessage() method of the SubscriptionClient class.
MS documentation says: "...When calling OnMessage, the client starts an internal message pump that constantly polls the queue or subscription. This message pump consists of an infinite loop that issues a Receive() call. If the call times out, it issues the next Receive() call. ..."
But when the application is running, the moment OnMessage() method is called only latest message(s) is received. When new messages are posted the constant polling does not seem to be working. After trying many different approaches the only way I could make this work and have the application react the moment new message is received is to place the code into a separate task with infinite loop. This seems totally wrong on so many levels! (see code below).
Can anyone help me to correct my code or post a working sample to accomplish the same functionality without the loop? Thank you!
public void ReceiveMessageFromSubscription(string topicName, string subscriptionFilter)
{
var newMessage = new MessageQueue();
int i = 0;
Task listener = Task.Factory.StartNew(() =>
{
while (true)
{
SubscriptionClient Client = SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionFilter);
Dictionary<string, string> retrievedMessage = new Dictionary<string, string>();
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
Client.OnMessage((message) =>
{
try
{
retrievedMessage.Add("messageGuid", message.Properties["MessageGuid"].ToString());
retrievedMessage.Add("instanceId", message.Properties["InstanceId"].ToString());
retrievedMessage.Add("pId", message.Properties["ProcessId"].ToString());
retrievedMessage.Add("processKey", message.Properties["ProcessKey"].ToString());
retrievedMessage.Add("message", message.Properties["Message"].ToString());
newMessage.AnnounceNewMessage(retrievedMessage); // event ->
message.Complete(); // Remove message from subscription.
}
catch (Exception ex)
{
string exmes = ex.Message;
message.Abandon();
}
}, options);
retrievedMessage.Clear();
i++;
Thread.Sleep(3000);
}
});
}
Your code has a few issues to iron out -
It falls through and I assume your application then exits - or at
least the thread that is listening for the messages terminates.
Your while loop keeps repeating the code to hook up the message handler,
you only need to do this once.
You need a way to keep the call stack alive and prevent your app from garbage collecting your object.
The below should see you on your way to success. Good luck.
ManualResetEvent CompletedResetEvent = new ManualResetEvent(false);
SubscriptionClient Client;
public void ReceiveMessagesFromSubscription(string topicName, string subscriptionFilter, string connectionString)
{
Task listener = Task.Factory.StartNew(() =>
{
// You only need to set up the below once.
Client = SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionFilter);
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
options.ExceptionReceived += LogErrors;
Client.OnMessage((message) =>
{
try
{
Trace.WriteLine("Got the message with ID {0}", message.MessageId);
message.Complete(); // Remove message from subscription.
}
catch (Exception ex)
{
Trace.WriteLine("Exception occurred receiving a message: {0}" + ex.ToString());
message.Abandon(); // Failed. Leave the message for retry or max deliveries is exceeded and it dead letters.
}
}, options);
CompletedResetEvent.WaitOne();
});
}
/// <summary>
/// Added in rudimentary exception handling .
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="ex">The <see cref="ExceptionReceivedEventArgs"/> instance containing the event data.</param>
private void LogErrors(object sender, ExceptionReceivedEventArgs ex)
{
Trace.WriteLine("Exception occurred in OnMessage: {0}" + ex.ToString());
}
/// <summary>
/// Call this to stop the messages arriving from subscription.
/// </summary>
public void StopMessagesFromSubscription()
{
Client.Close(); // Close the message pump down gracefully
CompletedResetEvent.Set(); // Let the execution of the listener complete and terminate gracefully
}
Alternatively you can chunk the message off in a more traditional fashion yourself using ReceiveBatch:
var messages = await queueClient.ReceiveBatchAsync(10, TimeSpan.FromSeconds(30),
cancellationToken);
I have a MQTT calls inside a loop and in each iteration, it should return a response from the subscriber so that I could use the value being forwarded after I published. But the problem is I don't know how would I do it.
I hope you have an idea there or maybe if I'm just not implementing it right, may you guide me through this. Thanks.
Here's my code:
// MyClientMgr
class MyClientMgr{
public long CurrentOutput { get; set; }
public void GetCurrentOutput(MyObjectParameters parameters, MqttClient client)
{
MyMessageObject msg = new MyMessageObject
{
Action = MyEnum.GetOutput,
Data = JsonConvert.SerializeObject(parameters)
}
mq_GetCurrentOutput(msg, client);
}
private void mq_GetCurrentOutput(MyMessageObject msg, MqttClient client)
{
string msgStr = JsonConvert.SerializeObject(msg);
client.Publish("getOutput", Encoding.UTF8.GetBytes(msgStr),
MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
client.MqttMsgPublishReceived += (sender, e) =>{
MyObjectOutput output = JsonConvert.DeserializeObject<MyObjectOutput>(Encoding.UTF8.GetString(e.Message));
CurrentOutput = output;
};
}
}
// MyServerMgr
class MyServerMgr
{
public void InitSubscriptions()
{
mq_GetOutput();
}
private void mq_GetOutput()
{
MqttClient clientSubscribe = new MqttClient(host);
string clientId = Guid.NewGuid().ToString();
clientSubscribe.Connect(clientId);
clientSubscribe.Subscribe(new string[] { "getOutput" }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
MqttClient clientPublish = new MqttClient(host);
string clientIdPub = Guid.NewGuid().ToString();
clientPublish.Connect(clientIdPub);
clientSubscribe.MqttMsgPublishReceived += (sender, e) => {
MyMessageObj msg = JsonConvert.DeserializeObject<MyMessageObj>(Encoding.UTF8.GetString(e.Message));
var output = msg.Output;
clientPublish.Publish("getOutput", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(output)), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
}
}
}
// MyCallerClass
class MyCallerClass
{
var host = "test.mqtt.org";
var myServer = new MyServerMgr(host);
var myClient = new MyClientMgr();
myServer.InitSubscriptions();
MqttClient client = new MqttClient(host);
for(int i = 0; i < 10; i++)
{
long output = 0;
MyObjectParameters parameters = {};
myClient.GetCurrentOutput(parameters, client) // here I call the method from my client manager
// to publish the getting of the output and assigned
// below for use, but the problem is the value doesn't
// being passed to the output variable because it is not
// yet returned by the server.
// Is there a way I could wait the process to
// get the response before assigning the output?
output = myClient.CurrentOutput; // output here will always be null
// because the response is not yet forwarded by the server
}
}
I have a loop in my caller class to call the mqtt publish for getting the output, but I have no idea how to get the output before it was assigned, I want to wait for the response first before going to the next.
I've already tried doing a while loop inside like this:
while(output == 0)
{
output = myClient.CurrentOutput;
}
Yes, I can get the output here, but it will slow down the process that much. And sometimes it will fail.
Please help me. Thanks.
It looks like you are trying to do synchronous communication over an asynchronous protocol (MQTT).
By this I mean you want to send a message and then wait for a response, this is not how MQTT works as there is no concept of a reply to a message at the protocol level.
I'm not that familiar with C# so I'll just give an abstract description of possible solution.
My suggestion would be to use a publishing thread, wait/pulse (Look at the Monitor class) to have this block after each publish and have the message handler call pulse when it has received the response.
If the response doesn't contain a wait to identify the original request you will also need a state machine variable to record which request is in progress.
You may want to look at putting a time out on the wait in case the other end does not respond for some reasons.
You can use AutoResetEvent class that has WaitOne() and Set() methods. Using WaitOne() after publish will wait until the message is published and using Set() under client_MqttMsgPublishReceived event will release the wait when the subscriber received the message he subscribed for.
I have built a peer to peer C# video conferencing application that uses a specific TCP port(17500) for audio communication. Currently, on my application interface, I enter the other IP address which has the program opened in order to communicate. What I want to do is to find the IP addresses automatically.
So, I though the best way to achieve this is to obtain the local IP addresses that are using the same TCP port number, 17500. How can I do that ? or is there any other methods getting IP addresses using the same application ?
As mentioned in comments, you need some kind of peer-discovery protocol.
As many multimedia devices, routers etc. use multicast based discovery protocols like SSDP, I created a similar discovery service sample .
Usage is simple. Just use
Discoverer.PeerJoined = ip => Console.WriteLine("JOINED:" + ip);
Discoverer.PeerLeft= ip => Console.WriteLine("LEFT:" + ip);
Discoverer.Start();
All your clients will use the same code.
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Caching; // add this library from the reference tab
using System.Text;
using System.Threading.Tasks;
namespace SO
{
public class Discoverer
{
static string MULTICAST_IP = "238.212.223.50"; //Random between 224.X.X.X - 239.X.X.X
static int MULTICAST_PORT = 2015; //Random
static UdpClient _UdpClient;
static MemoryCache _Peers = new MemoryCache("_PEERS_");
public static Action<string> PeerJoined = null;
public static Action<string> PeerLeft = null;
public static void Start()
{
_UdpClient = new UdpClient();
_UdpClient.Client.Bind(new IPEndPoint(IPAddress.Any, MULTICAST_PORT));
_UdpClient.JoinMulticastGroup(IPAddress.Parse(MULTICAST_IP));
Task.Run(() => Receiver());
Task.Run(() => Sender());
}
static void Sender()
{
var IamHere = Encoding.UTF8.GetBytes("I AM ALIVE");
IPEndPoint mcastEndPoint = new IPEndPoint(IPAddress.Parse(MULTICAST_IP), MULTICAST_PORT);
while (true)
{
_UdpClient.Send(IamHere, IamHere.Length, mcastEndPoint);
Task.Delay(1000).Wait();
}
}
static void Receiver()
{
var from = new IPEndPoint(0, 0);
while (true)
{
_UdpClient.Receive(ref from);
if (_Peers.Add(new CacheItem(from.Address.ToString(), from),
new CacheItemPolicy() {
SlidingExpiration = TimeSpan.FromSeconds(20),
RemovedCallback = (x) => { if (PeerLeft != null) PeerLeft(x.CacheItem.Key); }
}
)
)
{
if (PeerJoined != null) PeerJoined(from.Address.ToString());
}
Console.WriteLine(from.Address.ToString());
}
}
}
}
Now a little bit about the algorithm:
Every client multicasts a packet every seconds.
if the receiver(every client has it) gets a packet from an IP that isn't in its cache, it will fire PeerJoined method.
Cache will expire in 20 seconds. If a client doesn't receive a packet within that duration from another client in cache, it will fire PeerLeft method.
I believe if you are using a peer to peer application to exchange packets, when you need to know if someone "Is Online and Ready for connection", you need to send a broadcast. We can do it easily using an UDP connection.
I'll post an example where you use two methods: one to ask the entire network for ready clients in a broadcast message, and the other will start a listener to answer back broadcast asking message, or start a connection if a response of type "i am here" comes.
Hope it helps!
public sealed class UdpUtility
{
// Our UDP Port
private const int broadcastPort = 11000;
// Our message to ask if anyone is ready for connection
private const string askMessage = "ARE ANYONE OUT THERE?";
// Our answer message
private const string responseMessage = "I AM HERE!";
// We use this method to look for a client to connect with us.
// It will send a broadcast to the network, asking if any client is ready for connection.
public void SendBroadcastMessage()
{
var udp = new UdpClient(broadcastPort);
var endpoint = new IPEndPoint(IPAddress.Broadcast, broadcastPort);
try
{
var bytes = Encoding.ASCII.GetBytes(askMessage);
udp.Send(bytes, bytes.Length, endpoint);
}
catch (Exception ex)
{
// Treat your connection exceptions here!
}
}
// This method will start a listener on the port.
// The client will listen for the ask message and the ready message.
// It can then, answer back with a ready response, or start the TCP connection.
public void ListenBroadcastMessage()
{
var udp = new UdpClient(broadcastPort);
var endpoint = new IPEndPoint(IPAddress.Broadcast, broadcastPort);
bool received = false;
try
{
while (!received)
{
// We start listening broadcast messages on the broadcast IP Address interface.
// When a message comes, the endpoing IP Address will be updated with the sender IP Address.
// Then we can answer back the response telling that we are here, ready for connection.
var bytes = udp.Receive(ref endpoint);
var message = Encoding.ASCII.GetString(bytes);
if (message == askMessage)
{
// Our client received the ask message. We must answer back!
// When the client receives our response, his endpoint will be updated with our IP Address.
// The other client can, then, start the TCP connection and do the desired stuff.
var responseBytes = Encoding.ASCII.GetBytes(responseMessage);
udp.Send(responseBytes, responseBytes.Length, endpoint);
}
else if (message == responseMessage)
{
// We received a connection ready message! We can stop listening.
received = true;
// We received a response message!
// We can start our TCP connection here and do the desired stuff.
// Remember: The other client IP Address (the thing you want) will be on the
// endpoint object at this point. Just use it and start your TCP connection!
}
}
}
catch (Exception ex)
{
// Treat your connection exceptions here!
}
}
}
Invoke your command prompt to do "netstat -n" and extract the output.
Here is a piece of code taken from a program that I have wrote modified to fit your requirements. You will still need to further process the data to get the IP addresses
Process netP = new Process();
ProcessStartInfo netPI = new ProcessStartInfo();
netPI.FileName = "cmd";
netPI.UseShellExecute = false;
netPI.RedirectStandardOutput = true;
netPI.RedirectStandardInput = true;
netPI.RedirectStandardError = true;
netPI.CreateNoWindow = true;
netP.StartInfo = NetPI;
netP.Start();
while (!netP.Start())
Thread.Sleep(100);
StreamWriter sW = netP.StandardInput;
StreamReader sR = netP.StandardOutput;
sW.WriteLine("netstat -n")
sW.Close();
string data = sR.ReadToEnd();
sR.Close();
//Do some further processing to filter out the addresses and extract