Cannot get ReadyReceive pub-sub to work using NetMQ 4.x - c#

I created 2 simple C# Console Projects (.net 4.5.2), added the v4.0.0.1 NetMQ Nuget package to each, loaded each program up into separate Visual Studio 2017 Community Editions, put a breakpoint on the 1 line contained within the OnReceiveReady callback method, started the subscriber program first, then started the publisher program. The ReceieveReady event is not being triggered in the subscriber. What am I doing wrong? Even if I chose subSocket.Subscribe("") then I still didn't get any messages received. Also, removing/modifying the Send/Receive HighWatermarks didn't change things either. Thanks for your help!
Here's the Publisher code:
using System;
using NetMQ;
using NetMQ.Sockets;
using System.Threading;
namespace SampleNQPub
{
class Program
{
static void Main(string[] args)
{
var addr = "tcp://127.0.0.1:3004";
using (var pubSocket = new PublisherSocket())
{
Console.WriteLine("Publisher socket binding.");
pubSocket.Options.SendHighWatermark = 10;
pubSocket.Bind(addr);
for (int i=0; i < 30; i++)
{
pubSocket.SendMoreFrame("NQ").SendFrame(i.ToString());
Thread.Sleep(1000);
}
pubSocket.Disconnect(addr);
}
}
}
}
Here's the Subscriber code:
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
namespace SampleNQSub
{
class Program
{
static void Main(string[] args)
{
var addr = "tcp://127.0.0.1:3004";
using (var subSocket = new SubscriberSocket())
{
subSocket.ReceiveReady += OnReceiveReady;
subSocket.Options.ReceiveHighWatermark = 10;
subSocket.Connect(addr);
subSocket.Subscribe("NQ");
for (int i=0; i < 20; i++)
{
Thread.Sleep(1000);
}
subSocket.Disconnect(addr);
}
}
static void OnReceiveReady(object sender, NetMQSocketEventArgs e)
{
var str = e.Socket.ReceiveFrameString();
}
}
}

Ok, this is a gotcha question in the NetMQ world and I just figured it out. You MUST setup a NetMQPoller that will wind up calling every ReceiveReady callback which you have added to it (NetMQPoller).
Here is the corrected code which will at least (i.e., ReceiveFrameString still only getting the "NQ" part but that's just another method call to fix) get the ReceiveReady event triggered:
using System.Threading;
using System.Threading.Tasks;
using NetMQ;
using NetMQ.Sockets;
namespace SampleNQSub
{
class Program
{
static void Main(string[] args)
{
var addr = "tcp://127.0.0.1:3004";
NetMQPoller poller = new NetMQPoller();
using (var subSocket = new SubscriberSocket())
{
subSocket.ReceiveReady += OnReceiveReady;
subSocket.Options.ReceiveHighWatermark = 10;
subSocket.Connect(addr);
subSocket.Subscribe("NQ");
poller.Add(subSocket);
poller.RunAsync();
for (int i = 0; i < 20; i++)
{
Thread.Sleep(1000);
}
subSocket.Disconnect(addr);
}
}
static void OnReceiveReady(object sender, NetMQSocketEventArgs e)
{
var str = e.Socket.ReceiveFrameString();
e.Socket.ReceiveMultipartStrings();
}
}
}
I noticed that the authors of NetMQ decided in 4.x to take care of the Context object internally so the user wouldn't have to bare the burden of managing it. It would be nice also if they could hide this "polling pump" code from the user as well for the most simple use case.
As a comparison, take a look at the subscriber using NodeJS (with the zmq library) utilizing the Publisher console app I posted above (save this code to sub.js and, in a Windows console, type 'node sub.js'):
var zmq = require('zmq'), sock = zmq.socket('sub');
sock.connect('tcp://127.0.0.1:3004');
sock.subscribe('NQ');
console.log('Subscriber connected to port 3004');
sock.on('message', function() {
var msg = [];
Array.prototype.slice.call(arguments).forEach(function(arg) {
msg.push(arg.toString());
});
console.log(msg);
});
So where's the poller pump mechanism in this? (Answer: I don't care! I just want the messages supplied to me in a callback that I register. [Obviously, tongue-in-cheek. I get that a NetMQPoller is versatile and handles more complex issues, but for basic "give me a message in a callback when it arrives", it would be nice if it were handled internally by the library.])

Related

c# interprocess communication between .net 6 and framwork 4.8

WCF is really easy to make, literally i think 10 lines you can setup the WCF, but just one problem....i did not work in .net 6, i tried, was happy, until i run the app, he compiles with the exact same code from 4.8 but start generating exception after exception
and after some google, it seams that .net 6 (core) did not support WCF anymore
so what is the best way to make a desktop .net framework 4.8 app and a .net 6 WPF app communicate between then, exchanging some flag and variables
the simplest way possible, preferable one unique way that can be implemented in both 4.8 and 6.0, but i don't mind if is different technologies in both end if it works and is simple
I would prefer Interprocess-Communication via NetNamedPipes (NamedPipeServerStream and NamedPipeClientStream) and using Protobuf serialization.
for people suffering with that, abandon WCF if you just wanna send/get simple flags/infos/strings use NamedPipeServerStream and NamedPipeClientStream as #egal_reloaded posted, some (ugly) code samples:
4.8:
server class:
using System;
using System.Collections.Generic;
using System.IO.Pipes;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PipeTetst_FrameWork48_WCF
{
public class NamedPipeStreamServer
{
public void server()
{
var pipeServer = new NamedPipeServerStream("testpipe481", PipeDirection.InOut, 4);
StreamReader sr = new StreamReader(pipeServer);
StreamWriter sw = new StreamWriter(pipeServer);
do
{
try
{
pipeServer.WaitForConnection();
string test;
sw.WriteLine("Waiting");
sw.Flush();
pipeServer.WaitForPipeDrain();
test = sr.ReadLine();
MessageBox.Show(string.Format("Received from client: {0}", test));
}
catch (Exception ex) { throw ex; }
finally
{
pipeServer.WaitForPipeDrain();
if (pipeServer.IsConnected) { pipeServer.Disconnect(); }
}
} while (true);
}
}
}
client class:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PipeTetst_FrameWork48_WCF
{
public class NamedPipeStreamClient
{
public void client()
{
var pipeClient = new NamedPipeClientStream(".",
"testpipe482", PipeDirection.InOut, PipeOptions.None);
if (pipeClient.IsConnected != true)
{
pipeClient.Connect();
}
StreamReader sr = new StreamReader(pipeClient);
StreamWriter sw = new StreamWriter(pipeClient);
string temp;
temp = sr.ReadLine();
if (temp == "Waiting")
{
try
{
sw.WriteLine("Test Message");
sw.Flush();
pipeClient.Close();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
}
two button on a form in a TASK to not block the app(freeze):
private void button5_Click(object sender, EventArgs e)
{
NamedPipeStreamServer server = new NamedPipeStreamServer();
Task.Run(() => server.server()).ContinueWith(
_ =>
{
MessageBox.Show("asd Server");
}); // Scheduled to the ThreadPool
}
private void button6_Click(object sender, EventArgs e)
{
NamedPipeStreamClient client = new NamedPipeStreamClient();
Task.Run(() => client.client()).ContinueWith(
_ =>
{
MessageBox.Show("asd client");
}); // Scheduled to the ThreadPool
}
and the best part, this code os for .net framwork 4.8 traditional and .net 6 is the exact same code and WORKS!!
of course, you need to have 2 of this code above in 2 app different, and set the name of the pipes right
and the best part NO need to waste days trying to make something broken and unfinished like CoreWCF that need way too much code and is not reliable and only works in some frameworks and in each has a different code, and was almost 0 support they only have a half asset samples on GitHub that don't help with anything

C# ServerManager leaking memory on Windows 7

I have been trying to diagnose a memory leak in a service which only appears on Windows 7/Server 2008 R2. I narrowed it down to where we are using Microsoft.Web.Administration.ServerManager to gather info about the apps in our site. I whittled it down to the console app below, which exhibits the same behavior. It might still be more complex than it needs to be, but I wanted to emulate the behavior of the service as much as possible.
I found a previous question here that was very similar and made the changes suggested in the answers. This appeared to reduce the rate of growth, but it still leaks significantly (under the comments "Original Test" I have commented out code that I changed based on those answers. the "Modified Test" comments indicate the changes I made. I didn't initially have the GC.Collect call in, and when I ran this on a Windows 10 system, it grew for quite some time before the garbage collection kicked in. With the GC.Collect call in place, it ran without growing on Win 10, but on Win 7 it made no difference.
I ran it under a profiler that indicated the memory being leaked was native, and that the leak was coming from nativerd.dll.
Has anyone encountered a problem like this? I'm new to C# and am still learning how Garbage Collection works, so I'm wondering if there is something I'm doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Web.Administration;
namespace ServerManagerLeakTest
{
class Program
{
static void Main(string[] args)
{
Console.Write("Working.");
var me = new MyClass();
me.Run();
}
}
internal class MyClass
{
ServerManagerWrapper _smw = new ServerManagerWrapper();
public void Run()
{
while (true)
{
var t = Task.Run(async delegate
{
DoWork();
await Task.Delay(1000);
});
try
{
t.Wait();
}
catch (Exception e)
{
Console.Write("Main Exception: " + e.Message);
}
Console.Write(".");
}
}
public void DoWork()
{
try
{
var data = _smw.GetWebApps().ToList();
data.Clear();
}
catch (Exception e)
{
Console.Write("DoWork Exception: " + e.Message);
}
}
}
internal class ServerManagerWrapper
{
public List<int> GetWebApps()
{
List<int> result = new List<int>() { };
// Original Test
//
// using (var serverManager = new ServerManager())
// {
// foreach (var site in serverManager.Sites)
// {
// result.AddRange(GetWebApps(site));
// }
//}
// Modified Test
var serverManager = new ServerManager();
foreach (var site in serverManager.Sites)
{
result.AddRange(GetWebApps(site));
}
serverManager.Dispose();
serverManager = null;
System.GC.Collect();
return result;
}
private IEnumerable<int> GetWebApps(Site site)
{
// Original Test
//
//for (var application in site.Applications)
//{
// yield return application.GetHashCode();
//}
// Modified Test
List<int> result = new List<int>() { };
for (int i = 0; i < site.Applications.Count; i++)
{
result.Add(site.Applications[i].GetHashCode());
}
return result;
}
}
}
Answer provided in comments from #Lex Li.
Move the check to a separate process. Calling IIS REST API, PowerShell, or even appcmd and parse the result. Let the leak be out of your own service.

How do I assign this value to a global variable in c#?

I need to assign a value that's being received from a socket (TCP/IP) to a variable, so I can use it in a label in a form.
I'm asking here because I've been searching and trying for hours and can't find anything.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
namespace ConsoleApp1
{
class Exemys
{
static byte[] Buffer { get; set; }
static Socket sck;
[STAThread]
public static void Conectar(/*string[] args*/)
{
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndpoint = new IPEndPoint(IPAddress.Parse("192.168.34.230"), 5202);
try
{
sck.Connect(localEndpoint);
Console.WriteLine("Exemys connected!\r\n");
}
catch
{
Console.Write("Unable to connect to Exemys\r\n");
Conectar(/*args*/);
}
while (true)
{
Buffer = new byte[sck.SendBufferSize];
int bytesRead = sck.Receive(Buffer);
byte[] formatted = new byte[bytesRead];
for (int i = 0; i < bytesRead; i++)
{
formatted[i] = Buffer[i];
}
string mensaje = Encoding.ASCII.GetString(formatted);
Console.Write(mensaje + "\r\n");
}
}
}
}
This code is written in a class, and the Form is in other place.
The value that I need to assign is mensaje, so I can see it in a Text Box in the Form.
First of all global variables smells like a bad design. Anyway, it seems like you are giving an example of a ConsoleApplication but you have maybe a Windows Form Application? Obviously you can not run a Form from a Console Application, so just convert everything to a Windows Forms Application to start with.
Anyway, after you have a Form working, you will probably could change your code so that Conectar(), that we in fact we can give a better name like ConnectAndGetMessage(), to actually return the message. Then you call that method from your Form, maybe on the Load handler.
Briefly, you could do something like this:
class Exemys {
public static string ConnectAndGetMessage() {
// Here your code! ;)
return mensaje;
}
}
class FormWithTextbox {
private void FormWithLabel_Load(object sender, System.EventArgs e)
{
Textbox1.Text = Exemys.ConnectAndGetMessage();
}
}
Please note that this is not the best solution because you should inject the Exemys as a dependency instead, anyway, that will be for a later improvement.
Another thing to point out about your code, but you will probably get to know quickly is that you are not exiting the while loop, so you will probably end up in an infinite loop.

Why does a bound SUB receive only one message from a connecting PUB?

I'm making examples for my ZeroMQ CLR namespace, however I have a problem with PUB/SUB.
Why do I get only the first message? Sometimes I get no message, if I debug through the client (on PubSub_Client(arg);) I get some messages.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Security.Cryptography;
using ZeroMQ;
namespace ZeroMQ.Test
{
static partial class Program
{
static string PubSub_FrontendAddress = "tcp://127.0.0.1:2772";
public static void Main(string[] args)
{
if (args == null || args.Length < 1)
{
// say here were some arguments...
args = new string[] { "World" };
}
// Setup the ZContext
context = ZContext.Create();
CancellationTokenSource cancellor0 = null;
{
// Create the "Server" cancellor and threads
cancellor0 = new CancellationTokenSource();
var serverThread = new Thread(PubSub_Server);
serverThread.Start(cancellor0.Token);
serverThread.Join(64);
}
{
Thread.Sleep(1000);
Console.WriteLine("Starting...");
// foreach arg we are the Client, asking the Server
foreach (string arg in args)
{
PubSub_Client(arg);
// Thread.Sleep(1000);
}
Console.WriteLine("Ended...");
}
if (cancellor0 != null)
{
// Cancel the Server
cancellor0.Cancel();
}
// we could have done here context.Terminate()
}
static void PubSub_Server(object cancelluS)
{
var cancellus = (CancellationToken)cancelluS;
using (var socket = ZSocket.Create(context, ZSocketType.SUB))
{
socket.Bind(PubSub_FrontendAddress);
socket.SubscribeAll();
/* var poller = ZPollItem.Create(socket, (ZSocket _socket, out ZMessage message, out ZError _error) =>
{
while (null == (message = _socket.ReceiveMessage(/* ZSocketFlags.DontWait, * out _error)))
{
if (_error == ZError.EAGAIN)
{
_error = ZError.None;
Thread.Sleep(1);
continue;
}
throw new ZException(_error);
}
return true;
}); /**/
while (!cancellus.IsCancellationRequested)
{
ZError error;
ZMessage request;
/* if (!poller.TryPollIn(out request, out error, TimeSpan.FromMilliseconds(512)))
{
if (error == ZError.EAGAIN)
{
error = ZError.None;
Thread.Sleep(1);
continue;
}
throw new ZException(error);
} /**/
if (null == (request = socket.ReceiveMessage(ZSocketFlags.DontWait, out error)))
{
if (error == ZError.EAGAIN)
{
error = ZError.None;
Thread.Sleep(1);
continue;
}
throw new ZException(error);
} /**/
foreach (ZFrame frame in request)
{
string strg = frame.ReadString();
Console.WriteLine("{0} said hello!", strg);
}
}
socket.Unbind(PubSub_FrontendAddress);
}
}
static void PubSub_Client(string name)
{
using (var socket = ZSocket.Create(context, ZSocketType.PUB))
{
using (var crypto = new RNGCryptoServiceProvider())
{
var identity = new byte[8];
crypto.GetBytes(identity);
socket.Identity = identity;
}
socket.Connect(PubSub_FrontendAddress);
using (var request = new ZMessage())
{
request.Add(new ZFrame(name));
socket.Send(request);
}
socket.Disconnect(PubSub_FrontendAddress);
}
}
}
}
I'm having trouble with your design which seems just wrong:
A single subscriber and multiple publishers is an odd choice. I trust you have a good reason for it, but you should have said what that is. When sending messages from multiple clients to a single server, it is normal to use DEALER/ROUTER sockets instead. PUB/SUB is intended for a small set of publishers to a large number of subscribers.
A client that connects, sends one message, then immediately disconnects, is another very unusual use case that I hope is just an example:
For one thing, you are open to linger problems whereby the message will get dropped on the disconnect it is isn't sent within the linger timeout. [I don't know what the default linger is for your language binding, so that may or may not be an issue, but you should at least check to ensure that it isn't.]
For another, as you've already found, there are issues around the time it takes to connect to a socket, which may lead to PUB messages getting dropped if they are sent before the socket has properly connected.
If you insist on using PUB/SUB in this manner, you will need an out of band protocol to synchronise the PUB and SUB threads before the pub messages are sent. There are examples of how to do this reliable pub/sub in the zeromq guide. This will involve a second set of sockets in the same threads to send the synchronisation messages; DEALER sockets don't drop messages which is why they are suitable for that purpose...
But, DEALER/ROUTER sockets would appear to be a better choice than PUB/SUB unless there is some design requirement that hasn't been disclosed.
Well... There was a comment by Martin Sustrik: "The problem is that connecting is asynchronous and takes certain amount of time."
Now there is Thread.Sleep(64) - and it works...:
static void PubSub_Client(string name)
{
using (var socket = ZSocket.Create(context, ZSocketType.PUB))
{
socket.Connect(PubSub_FrontendAddress);
Thread.Sleep(64);
using (var request = new ZMessage())
{
request.Add(new ZFrame(name));
socket.Send(request);
}
socket.Disconnect(PubSub_FrontendAddress);
}
}
Do you know any better way to get the connection established?

C# Skype API Video Call

I was working on a security monitor application and the best approach i found was Skype.
when a possible intrusion occurs the application calls a specified Skype ID which is probably my android phone i am done with all the image processing stuff. But i am stuck with this Skype API i wrote this piece of code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SKYPE4COMLib;
namespace SkypeCall
{
class Program
{
static void Main(string[] args)
{
Skype skype;
skype = new Skype("Skype4COM.Skype", "Skype_");
Call call = skype.PlaceCall(SkypeID);
call.StartVideoSend();
}
}
}
This initiates a voice call but in the call.StartVideoSend(); shows an error
An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in SkypeCall.exe
Additional information: CALL: Action failed
i even tried this but i guess that's old API and couldn't get anything out of it.
And not even by sending commands .
if somebody would help me out i'll be grateful.
I think you need to wait until the call is connected.
easiest way would be to test the call.Status
class Program
{
static void Main(string[] args)
{
Skype skype;
skype = new SKYPE4COMLib.Skype();
string SkypeID = args[1];
Call call = skype.PlaceCall(SkypeID);
do
{
System.Threading.Thread.Sleep(1);
} while (call.Status != TCallStatus.clsInProgress);
call.StartVideoSend();
}
}
You could also add an event, however I think this will fire on every call so unless you are only using it for this project it might be too much.
class Program
{
static string SkypeID = "";
static void Main(string[] args)
{
Skype skype;
skype = new SKYPE4COMLib.Skype();
skype.CallStatus += new _ISkypeEvents_CallStatusEventHandler(skype_CallStatus);
Call call = skype.PlaceCall(SkypeID);
Console.ReadKey();
}
static void skype_CallStatus(Call pCall, TCallStatus Status)
{
if (Status == TCallStatus.clsInProgress && pCall.PartnerHandle == SkypeID) { pCall.StartVideoSend(); }
}
}

Categories