NetMQ client to client messaging - zeromq

I'm trying to create an rpc program to communicate hosts located on different networks and chose Router-Dealer configuration of NetMQ provided here: http://netmq.readthedocs.io/en/latest/router-dealer/#router-dealer
But the problem is that router always selects a random dealer when routing a message to backend.
Code which I used :
using (var frontend = new RouterSocket(string.Format("#tcp://{0}:{1}", "127.0.0.1", "5556")))//"#tcp://10.0.2.218:5559"
using (var backend = new DealerSocket(string.Format("#tcp://{0}:{1}", "127.0.0.1", "5557")))//"#tcp://10.0.2.218:5560"
{
// Handler for messages coming in to the frontend
frontend.ReceiveReady += (s, e) =>
{
Console.WriteLine("message arrived on frontEnd");
NetMQMessage msg = e.Socket.ReceiveMultipartMessage();
string clientAddress = msg[0].ConvertToString();
Console.WriteLine("Sending to :" + clientAddress);
//TODO: Make routing here
backend.SendMultipartMessage(msg); // Relay this message to the backend };
// Handler for messages coming in to the backend
backend.ReceiveReady += (s, e) =>
{
Console.WriteLine("message arrived on backend");
var msg = e.Socket.ReceiveMultipartMessage();
frontend.SendMultipartMessage(msg); // Relay this message to the frontend
};
using (var poller = new NetMQPoller { backend, frontend })
{
// Listen out for events on both sockets and raise events when messages come in
poller.Run();
}
}
Code for Client:
using (var client = new RequestSocket(">tcp://" + "127.0.0.1" + ":5556"))
{
var messageBytes = UTF8Encoding.UTF8.GetBytes("Hello");
var messageToServer = new NetMQMessage();
//messageToServer.AppendEmptyFrame();
messageToServer.Append("Server2");
messageToServer.Append(messageBytes);
WriteToConsoleVoid("======================================");
WriteToConsoleVoid(" OUTGOING MESSAGE TO SERVER ");
WriteToConsoleVoid("======================================");
//PrintFrames("Client Sending", messageToServer);
client.SendMultipartMessage(messageToServer);
NetMQMessage serverMessage = client.ReceiveMultipartMessage();
WriteToConsoleVoid("======================================");
WriteToConsoleVoid(" INCOMING MESSAGE FROM SERVER");
WriteToConsoleVoid("======================================");
//PrintFrames("Server receiving", clientMessage);
byte[] rpcByteArray = null;
if (serverMessage.FrameCount == 3)
{
var clientAddress = serverMessage[0];
rpcByteArray = serverMessage[2].ToByteArray();
}
WriteToConsoleVoid("======================================");
Console.ReadLine();
}
Code for Dealer:
using (var server = new ResponseSocket())
{
server.Options.Identity = UTF8Encoding.UTF8.GetBytes(confItem.ResponseServerID);
Console.WriteLine("Server ID:" + confItem.ResponseServerID);
server.Connect(string.Format("tcp://{0}:{1}", "127.0.0.1", "5557"));
using (var poller = new NetMQPoller { server })
{
server.ReceiveReady += (s, a) =>
{
byte[] response = null;
NetMQMessage serverMessage = null;
try
{
serverMessage = a.Socket.ReceiveMultipartMessage();
}
catch (Exception ex)
{
Console.WriteLine("Exception on ReceiveMultipartMessage : " + ex.ToString());
//continue;
}
byte[] eaBody = null;
string clientAddress = "";
if (serverMessage.FrameCount == 2)
{
clientAddress = serverMessage[0].ConvertToString();
Console.WriteLine("ClientAddress:" + clientAddress);
eaBody = serverMessage[1].ToByteArray();
Console.WriteLine("Received message from remote computer: {0} bytes , CurrentID : {1}", eaBody.Length, confItem.ResponseServerID);
}
else
{
Console.WriteLine("Received message from remote computer: CurrentID : {0}", confItem.ResponseServerID);
}
};
poller.Run();
}
}
Is it possible to choose a specific backend on frontend.ReceiveReady?
Thanks!

Your backend should be router as well. You need the worker to register or you need to know all the available workers and their identity. When send on the backend push the worker identity at the beginning of the server.
Take a look at the Majordomo example in the zeromq guide:
http://zguide.zeromq.org/page:all#toc72
http://zguide.zeromq.org/page:all#toc98

Related

How do you create a client using websocket-sharp?

I'm using ClientWebSocket to subscribe to a REST service but want to be able to use websocket-sharp instead.
static async void MonitorISY(string IPAddress, string userName, string password, IMessageWriter writer)
{
ClientWebSocket client = new ClientWebSocket();
client.Options.AddSubProtocol("ISYSUB");
client.Options.SetRequestHeader("Origin", "com.universal-devices.websockets.isy");
var auth = Convert.ToBase64String(Encoding.Default.GetBytes(userName + ":" + password));
client.Options.SetRequestHeader("Authorization", "Basic " + auth);
await client.ConnectAsync(new Uri("ws://" + IPAddress + "/rest/subscribe"), CancellationToken.None);
var receiveBufferSize = 512;
byte[] buffer = new byte[receiveBufferSize];
writer.Clear();
while (true)
{
var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
var resultJson = (new UTF8Encoding()).GetString(buffer);
writer.WriteLn(resultJson);
writer.WriteLn();
}
}
Here is my websocket-sharp attempt. When ws.Connect(); is called, I'm getting a Not a WebSocket handshake response error message. In the working code, I have to setup the Origin, SubProtocol and the RequestHeader. I think I'm doing that correctly for the websocket-sharp code, with the exception of the Request Header. I've been unable to find a working example that specifies authentication.
using (var nf = new Notifier())
using (var ws = new WebSocket("ws://172.16.0.40/rest/subscribe", "ISYSUB"))
{
ws.Log.Level = LogLevel.Trace;
var username = "user";
var password = "pass";
ws.Origin = "com.universal-devices.websockets.isy";
ws.SetCredentials(username, password, true);
ws.OnOpen += (sender, e) => ws.Send("Hi, there!");
ws.OnMessage += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = "WebSocket Message",
Body = !e.IsPing ? e.Data : "Received a ping.",
Icon = "notification-message-im"
}
);
ws.OnError += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = "WebSocket Error",
Body = e.Message,
Icon = "notification-message-im"
}
);
ws.OnClose += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = String.Format("WebSocket Close ({0})", e.Code),
Body = e.Reason,
Icon = "notification-message-im"
}
);
ws.Connect();
I think the best example is https://github.com/sta/websocket-sharp/tree/master/Example3
Although I did have to make a few tiny adjustments to get it to compile in Visual Studio 2017 Enterprise.
The index.html is based on http://www.websocket.org/echo.html

Catch incoming emails and send them to a web service (rather than just to a mail server)

I would like to catch incoming emails and send them a web service (rather than just to a mail server).
--
After some searching I found a way of getting new emails via polling - see below: This may be of some help to others. Is there a way to receive messages by SMTP? Perhaps by ISAPI ???
using Limilabs.Mail;
using Limilabs.Client.IMAP;
public ActionResult checkIMAPmail()
{
string rval = "not a sausage";
using (Imap imap = new Imap())
{
imap.Connect(<mail server>);
imap.Login(<username>, <password>);
imap.SelectInbox();
List<long> uids = imap.Search(Flag.Unseen);
foreach (long uid in uids)
{
byte[] ourBytes = imap.GetMessageByUID(uid);
IMail email = new MailBuilder().CreateFromEml(ourBytes);
rval = email.Subject + " [" + email.From + "][" + email.Text + "]";
}
imap.Close();
}
return Content(rval, "text/html");
}
See also http://stackoverflow.com/questions/670183/accessing-imap-in-c-sharp
for other IMAP packages, although note the change to using byte[], above.
Given that Limilabs.Mail is a paid service, I finally used MailKit:
using MailKit;
public int checkIMAPmail()
{
int numEmails = 0;
try {
using (var client = new MailKit.Net.Imap.ImapClient())
{
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(ourSmtpClient);
// disable the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(ourSmtpAdminUser, ourSmtpAdminUserPwd);
// The Inbox folder is always available on all IMAP servers...
var inboxFolder = client.Inbox;
var savedFolder = client.GetFolder("saved");
inboxFolder.Open(FolderAccess.ReadWrite);
for (int ii = 0; ii < inboxFolder.Count; ii++)
{
var query = MailKit.Search.SearchQuery.NotSeen;
foreach (var uid in inboxFolder.Search(query))
{
var thisMsg = inboxFolder.GetMessage(uid);
string thisDate = notNullString(thisMsg.Date);
string thisSubject = notNullString( thisMsg.Subject);
string thisBody = notNullString(thisMsg.GetTextBody(0)); // plain text
string thisFromName = "";
string thisFromEmail = "";
if ( thisMsg.From != null)
{
// just get the first
foreach( var mb in thisMsg.From.Mailboxes)
{
thisFromName = notNullString( mb.Name);
thisFromEmail = notNullString( mb.Address);
break;
}
}
numEmails += 1;
// move email to saved
inboxFolder.MoveTo(uid, savedFolder);
}
}
client.Disconnect(true);
}
}
catch (Exception exc)
{
log2file("checkIMAPmail Error: " + exc.ToString());
}
return numEmails;
}

NetMQ why is "SendReady" needed for Req-Rep?

I have a problem that I managed to fix... However I'm a little concerned as I don't really understand why the solution worked;
I am using NetMQ, and specifically a NetMQ poller which has a number of sockets, one of which is a REQ-REP pair.
I have a queue of requests which get dequeued into requests, and the server handles each request type as required and sends back an appropriate response. This had been working without issue, however when I tried to add in an additional request type the system stopped working as expected; what would occur is that the request would reach the server, the server would send the response... and the client would not receive it. The message would not be received at the client until the server was shut down (unusual behavior!).
I had been managing the REQ-REP pair with a flag that I set before sending a request, and reset on receipt of a reply. I managed to fix the issue by only triggering replies within the "SendReady" event of the REQ socket - this automagically fixed all of my issues, however I can't really find anything in the documentation that tells me why the socket might not have been in the "sendready" state, or what this actually does.
Any information that could be shed on why this is working now would be great :)
Cheers.
Edit: Source
Client:
"Subscribe" is run as a separate thread to the UI
private void Subscribe(string address)
{
using (var req = new RequestSocket(address + ":5555"))
using (var sub = new SubscriberSocket(address + ":5556"))
using (var poller = new NetMQPoller { req, sub })
{
// Send program code when a request for a code update is received
sub.ReceiveReady += (s, a) =>
{
var type = sub.ReceiveFrameString();
var reply = sub.ReceiveFrameString();
switch (type)
{
case "Type1":
manager.ChangeValue(reply);
break;
case "Type2":
string[] args = reply.Split(',');
eventAggregator.PublishOnUIThread(new MyEvent(args[0], (SimObjectActionEventType)Enum.Parse(typeof(MyEventType), args[1])));
break;
}
};
req.ReceiveReady += Req_ReceiveReady;
poller.RunAsync();
sub.Connect(address + ":5556");
sub.SubscribeToAnyTopic();
sub.Options.ReceiveHighWatermark = 10;
reqQueue = new Queue<string[]>();
reqQueue.Enqueue(new string[] { "InitialiseClient", "" });
req_sending = false;
while (programRunning)
{
if (reqQueue.Count > 0 && !req_sending)
{
req_sending = true;
string[] request = reqQueue.Dequeue();
Console.WriteLine("Sending " + request[0] + " " + request[1]);
req.SendMoreFrame(request[0]).SendFrame(request[1]);
}
Thread.Sleep(1);
}
}
}
private void Req_ReceiveReady(object sender, NetMQSocketEventArgs e)
{
var req = e.Socket;
var messageType = req.ReceiveFrameString();
Console.WriteLine("Received {0}", messageType);
switch (messageType)
{
case "Reply1":
// Receive action
break;
case "Reply2":
// Receive action
break;
case "Reply3":
// Receive action
break;
}
req_sending = false;
}
Server:
using (var rep = new ResponseSocket("#tcp://*:5555"))
using (var pub = new PublisherSocket("#tcp://*:5556"))
using (var beacon = new NetMQBeacon())
using (var poller = new NetMQPoller { rep, pub, beacon })
{
// Send program code when a request for a code update is received
rep.ReceiveReady += (s, a) =>
{
var messageType = rep.ReceiveFrameString();
var message = rep.ReceiveFrameString();
Console.WriteLine("Received {0} - Content: {1}", messageType, message);
switch (messageType)
{
case "InitialiseClient":
// Send
rep.SendMoreFrame("Reply1").SendFrame(repData);
break;
case "Req2":
// do something
rep.SendMoreFrame("Reply2").SendFrame("RequestOK");
break;
case "Req3":
args = message.Split(',');
if (args.Length == 2)
{
// Do Something
rep.SendMoreFrame("Reply3").SendFrame("RequestOK");
}
else
{
rep.SendMoreFrame("Ack").SendFrame("RequestError - incorrect argument format");
}
break;
case "Req4":
args = message.Split(',');
if (args.Length == 2)
{
requestData = //do something
rep.SendMoreFrame("Reply4").SendFrame(requestData);
}
else
{
rep.SendMoreFrame("Ack").SendFrame("RequestError - incorrect argument format");
}
break;
default:
rep.SendMoreFrame("Ack").SendFrame("Error");
break;
}
};
// setup discovery beacon with 1 second interval
beacon.Configure(5555);
beacon.Publish("server", TimeSpan.FromSeconds(1));
// start the poller
poller.RunAsync();
// run the simulation loop
while (serverRunning)
{
// todo - make this operate for efficiently
// push updated variable values to clients
foreach (string[] message in pubQueue)
{
pub.SendMoreFrame(message[0]).SendFrame(message[1]);
}
pubQueue.Clear();
Thread.Sleep(2);
}
poller.StopAsync();
}
You are using the Request socket from multiple threads, which is not supported. You are sending on the main thread and receiving on the poller thread.
Instead of using regular queue try to use NetMQQueue, you can add it to the poller and enqueue from the UI thread. Then the sending is happening on the poller thread as well as the receiving.
You can read the docs here:
http://netmq.readthedocs.io/en/latest/queue/
Only thing I can think of is that the REP socket is ready to send only after you actually received a message fully (all parts).

WCF authentication issue on a particular machine

I have an issue with my WCF service/application. I am using TCP binding with Windows Authentication (dynamically setting the credentials).
The service is hosted on machine A. With my test client application on machine B, C, D, and E, it works fine. When the test client application is run on machine A (on itself), it is telling me that I have invalid credentials when in fact they are correct.
When hosting the service on machine B, the test client on machine B itself authenticates fine. Even if the test client is on A, it authenticates itself successfully on B.
I would say there is something wrong with machine A but I cannot figure out what.
Any help?
Thanks in advance.
K
Client Config:
public static EndpointAddress BuildServiceEndpointAddress()
{
var host = Settings.Default.Host;
var port = Settings.Default.Port;
string endpointAddress;
if (port == 0)
endpointAddress = string.Format("net.tcp://{0}/Service", host);
else
endpointAddress = string.Format("net.tcp://{0}:{1}/Service", host, port);
Console.WriteLine("Endpoint: {0}", endpointAddress);
//var endpointAddressObj = new EndpointAddress(endpointAddress );
return new EndpointAddress(new Uri(endpointAddress), new DnsEndpointIdentity("MyCert"));
}
public static IChannelFactory<IService> BuildChannelFactory(EndpointAddress endpointAddress)
{
var netTcpBinding = new NetTcpBinding()
{
MaxReceivedMessageSize = int.MaxValue,
Security = new NetTcpSecurity()
{
Mode = SecurityMode.Transport,
Transport = new TcpTransportSecurity()
{
ClientCredentialType = TcpClientCredentialType.Windows,
}
},
TransferMode = TransferMode.Streamed
};
var factory = new ChannelFactory<IService>(netTcpBinding);
factory.Endpoint.Address = endpointAddress;
factory.Credentials.Windows.ClientCredential.Domain = Settings.Default.Domain;
factory.Credentials.Windows.ClientCredential.UserName = Settings.Default.Username;
factory.Credentials.Windows.ClientCredential.Password = Settings.Default.Password;
Console.WriteLine("Opening Channel Factory ... ");
factory.Open();
Console.WriteLine("Opening Channel Factory ... done");
return factory;
}
Service Config:
var netTcpBinding = new NetTcpBinding()
{
MaxReceivedMessageSize = int.MaxValue,
Security = new NetTcpSecurity()
{
Mode = SecurityMode.Transport,
Transport = new TcpTransportSecurity()
{
ClientCredentialType = TcpClientCredentialType.Windows,
}
},
TransferMode = TransferMode.Streamed
};
var endpoint = serviceHost.AddServiceEndpoint(
typeof(IService),
netTcpBinding,
new Uri(string.Format("net.tcp://0:{0}/Service", tcpPort)));
ServiceSecurityAuditBehavior newAudit = new ServiceSecurityAuditBehavior();
newAudit.AuditLogLocation = AuditLogLocation.Application;
newAudit.MessageAuthenticationAuditLevel = AuditLevel.SuccessOrFailure;
newAudit.ServiceAuthorizationAuditLevel = AuditLevel.SuccessOrFailure;
newAudit.SuppressAuditFailure = false;
serviceHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexTcpBinding(),
string.Format("net.tcp://localhost:{0}/Service/mex", tcpPort));
serviceHost.Description.Behaviors.Remove<ServiceSecurityAuditBehavior>();
serviceHost.Description.Behaviors.Add(newAudit);
serviceHost.Opening += (sender, eventArgs) => LogDebug("Opening Connection ...");
serviceHost.Opened += (sender, eventArgs) => LogDebug("Opened Connection ...");
serviceHost.Closing += (sender, eventArgs) => LogDebug("Closing connection ...");
serviceHost.Closed += (sender, eventArgs) => LogDebug("Closed connection ...");
serviceHost.Faulted += (sender, eventArgs) => LogError("Fault detected on WCF host");
I just fixed the issue!
I replaced:
return new EndpointAddress(new Uri(endpointAddress), new
DnsEndpointIdentity("MyCert"));
with
return new EndpointAddress(new Uri(endpointAddress));
since the certificate is not needed for windows authentication.
Although I still am confused why it worked on other machines.

Why ServiceStack.Redis throw error instead of trying to connect to another read instance?

I successfully installed Redis on two machines and made then work as Master-Slave.
I tested some code to check if replication work and everything is ok.
My client manager looks like
var manager = new PooledRedisClientManager(new[] { "MasterIP:6379" }, new[] { "MasterIP:6379", "SlaveIP:6379" });
But now i shutdown my master instance and when i test my code again i get an error like client cant connect to Master server.
p.s For read i use GetReadOnlyCacheClient();
I repeated my code and i notice that client first is getting Master (error cant connect), then when i run my code again client is getting Slave, then again when i run my code client is getting master and so on.
I downloaded source code on ServiceStack.Redis client. I just wanted to check when that error happens and here is the code.
private void Connect()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
SendTimeout = SendTimeout,
ReceiveTimeout = ReceiveTimeout
};
try
{
if (ConnectTimeout == 0)
{
socket.Connect(Host, Port);
}
else
{
var connectResult = socket.BeginConnect(Host, Port, null, null);
connectResult.AsyncWaitHandle.WaitOne(ConnectTimeout, true);
}
if (!socket.Connected)
{
socket.Close();
socket = null;
return;
}
Bstream = new BufferedStream(new NetworkStream(socket), 16 * 1024);
if (Password != null)
SendExpectSuccess(Commands.Auth, Password.ToUtf8Bytes());
db = 0;
var ipEndpoint = socket.LocalEndPoint as IPEndPoint;
clientPort = ipEndpoint != null ? ipEndpoint.Port : -1;
lastCommand = null;
lastSocketException = null;
LastConnectedAtTimestamp = Stopwatch.GetTimestamp();
if (ConnectionFilter != null)
{
ConnectionFilter(this);
}
}
catch (SocketException ex)
{
if (socket != null)
socket.Close();
socket = null;
HadExceptions = true;
var throwEx = new RedisException("could not connect to redis Instance at " + Host + ":" + Port, ex);
log.Error(throwEx.Message, ex);
throw throwEx;
}
}
I really dont understand this code, couze project is really big, but i think there is no If this server fails, then try to get from another host in read only server list if any other exist
I can make some kind of mine custom logic to check if fail to try to get another read only instance..but isnt this client supported to be ready about this issue?

Resources