// .split broker client_msg method // Process a request coming from a client. We implement MMI requests // directly here (at present, we implement only the mmi.service request): public void ClientMsg(ZFrame sender, ZMessage msg) { // service & body if (msg.Count < 2) { throw new InvalidOperationException(); } using (ZFrame serviceFrame = msg.Pop()) { Service service = RequireService(serviceFrame); // Set reply return identity to client sender msg.Wrap(sender.Duplicate()); //if we got a MMI Service request, process that internally if (serviceFrame.Length >= 4 && serviceFrame.ToString().StartsWith("mmi.")) { string returnCode; if (serviceFrame.ToString().Equals("mmi.service")) { string name = msg.Last().ToString(); returnCode = Services.ContainsKey(name) && Services[name].Workers > 0 ? "200" : "404"; } else { returnCode = "501"; } var client = msg.Unwrap(); msg.Clear(); msg.Add(new ZFrame(returnCode)); msg.Prepend(serviceFrame); msg.Prepend(new ZFrame(MdpCommon.MDPC_CLIENT)); msg.Wrap(client); Socket.Send(msg); } else { // Else dispatch the message to the requested Service service.Dispatch(msg); } } }
// .split service methods // Here is the implementation of the methods that work on a service: // Lazy constructor that locates a service by name or creates a new // service if there is no service already with that name. public Service RequireService(ZFrame serviceFrame) { if (serviceFrame == null) { throw new InvalidOperationException(); } string name = serviceFrame.ToString(); Service service; if (Services.ContainsKey(name)) { service = Services[name]; } else { service = new Service(this, name); Services[name] = service; //zhash_freefn(self->workers, id_string, s_worker_destroy); if (Verbose) { "I: added service: '{0}'".DumpString(name); } } return(service); }
// .skip // The recv method waits for a reply message and returns that to the // caller. // --------------------------------------------------------------------- // Returns the reply message or NULL if there was no reply. Does not // attempt to recover from a broker failure, this is not possible // without storing all unanswered requests and resending them all... public ZMessage Recv(CancellationTokenSource cancellor) { // Poll socket for a reply, with timeout var p = ZPollItem.CreateReceiver(); ZMessage msg; ZError error; // .split body of send // On any blocking call, {{libzmq}} will return -1 if there was // an error; we could in theory check for different error codes, // but in practice it's OK to assume it was {{EINTR}} (Ctrl-C): // Poll the client Message if (Client.PollIn(p, out msg, out error, Timeout)) { // If we got a reply, process it if (Verbose) { msg.DumpZmsg("I: received reply"); } // Don't try to handle errors, just assert noisily if (msg.Count < 4) { throw new InvalidOperationException(); } using (ZFrame empty = msg.Pop()) if (!empty.ToString().Equals(string.Empty)) { throw new InvalidOperationException(); } using (ZFrame header = msg.Pop()) if (!header.ToString().Equals(MdpCommon.MDPC_CLIENT)) { throw new InvalidOperationException(); } using (ZFrame replyService = msg.Pop()) {} return(msg); } else if (Equals(error, ZError.ETERM)) { "W: interrupt received, killing client...\n".DumpString(); cancellor.Cancel(); } else { if (Verbose) { "W: permanent error, abandoning Error: {0}".DumpString(error); } } return(null); }
static void Main(string[] args) { using (ZSocket request = new ZSocket(ZSocketType.REQ)) { request.Connect("tcp://localhost:50000"); Console.WriteLine($"Connect to server: tcp://localhost:50000"); for (int i = 0; i < 10; i++) { string postStr = $"Lynn {i}"; request.Send(new ZFrame(postStr)); Console.WriteLine($"Post {postStr} to server...."); using (ZFrame reply = request.ReceiveFrame()) { Console.WriteLine($"Receive reply: {reply.ToString()}"); } } } Console.Read(); }
static void Main(string[] args) { string url = "tcp://*:50000"; using (ZSocket receiver = new ZSocket(ZSocketType.REP)) { receiver.Bind(url); Console.WriteLine($"Server is binding to {url}"); while (true) { using (ZFrame message = receiver.ReceiveFrame()) { Console.WriteLine($"Receive message {message.ToString()}"); var reply = new ZFrame("Lynn"); Console.WriteLine($"Send reply: {reply.ToString()}"); receiver.Send(reply); }; } }; }
// .split service methods // Here is the implementation of the methods that work on a service: // Lazy constructor that locates a service by name or creates a new // service if there is no service already with that name. public Service RequireService(ZFrame serviceFrame) { if(serviceFrame == null) throw new InvalidOperationException(); string name = serviceFrame.ToString(); Service service; if (Services.ContainsKey(name)) { service = Services[name]; } else { service = new Service(this, name); Services[name] = service; //zhash_freefn(self->workers, id_string, s_worker_destroy); if (Verbose) "I: added service: '{0}'".DumpString(name); } return service; }
// .split send request and wait for reply // Here is the {{send}} method. It sends a request to the broker and gets // a reply even if it has to retry several times. It takes ownership of // the request message, and destroys it when sent. It returns the reply // message, or NULL if there was no reply after multiple attempts: public ZMessage Send(string service, ZMessage request, CancellationTokenSource cancellor) { if (request == null) { throw new InvalidOperationException(); } // Prefix request with protocol frames // Frame 1: "MDPCxy" (six bytes, MDP/Client x.y) // Frame 2: Service name (printable string) request.Prepend(new ZFrame(service)); request.Prepend(new ZFrame(MdpCommon.MDPC_CLIENT)); if (Verbose) { request.DumpZmsg("I: send request to '{0}' service:", service); } int retriesLeft = Retries; while (retriesLeft > 0 && !cancellor.IsCancellationRequested) { if (cancellor.IsCancellationRequested || (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { _context.Shutdown(); } // Copy the Request and send on Client ZMessage msgreq = request.Duplicate(); ZError error; if (!Client.Send(msgreq, out error)) { if (Equals(error, ZError.ETERM)) { cancellor.Cancel(); break; // Interrupted } } var p = ZPollItem.CreateReceiver(); ZMessage msg; // .split body of send // On any blocking call, {{libzmq}} will return -1 if there was // an error; we could in theory check for different error codes, // but in practice it's OK to assume it was {{EINTR}} (Ctrl-C): // Poll the client Message if (Client.PollIn(p, out msg, out error, Timeout)) { // If we got a reply, process it if (Verbose) { msg.DumpZmsg("I: received reply"); } if (msg.Count < 3) { throw new InvalidOperationException(); } using (ZFrame header = msg.Pop()) if (!header.ToString().Equals(MdpCommon.MDPC_CLIENT)) { throw new InvalidOperationException(); } using (ZFrame replyService = msg.Pop()) if (!replyService.ToString().Equals(service)) { throw new InvalidOperationException(); } request.Dispose(); return(msg); } else if (Equals(error, ZError.ETERM)) { cancellor.Cancel(); break; // Interrupted } else if (Equals(error, ZError.EAGAIN)) { if (--retriesLeft > 0) { if (Verbose) { "W: no reply, reconnecting...".DumpString(); } ConnectToBroker(); } else { if (Verbose) { "W: permanent error, abandoning".DumpString(); } break; // Give up } } } if (cancellor.IsCancellationRequested) { "W: interrupt received, killing client...\n".DumpString(); } request.Dispose(); return(null); }
// .split recv method // This is the {{recv}} method; it's a little misnamed because it first sends // any reply and then waits for a new request. If you have a better name // for this, let me know. // Send reply, if any, to broker and wait for next request. public ZMessage Recv(ZMessage reply, CancellationTokenSource cancellor) { if (reply == null && _expectReply) { throw new InvalidOperationException(); } if (reply != null) { if (_replyTo == null) { throw new InvalidOperationException(); } reply.Wrap(_replyTo); SendToBroker(MdpCommon.MdpwCmd.REPLY.ToHexString(), string.Empty, reply); } _expectReply = true; while (true) { if (cancellor.IsCancellationRequested || (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { _context.Shutdown(); } var p = ZPollItem.CreateReceiver(); ZMessage msg; ZError error; if (Worker.PollIn(p, out msg, out error, Heartbeat)) { using (msg) { // If we got a reply if (Verbose) { msg.DumpZmsg("I: received message from broker:"); } Liveness = MdpCommon.HEARTBEAT_LIVENESS; // Don't try to handle errors, just assert noisily if (msg.Count < 3) { throw new InvalidOperationException(); } using (ZFrame empty = msg.Pop()) { if (!empty.ToString().Equals("")) { throw new InvalidOperationException(); } } using (ZFrame header = msg.Pop()) { if (!header.ToString().Equals(MdpCommon.MDPW_WORKER)) { throw new InvalidOperationException(); } } //header.ReadString().Equals(MDPW_WORKER); using (ZFrame command = msg.Pop()) { if (command.StrHexEq(MdpCommon.MdpwCmd.REQUEST)) { // We should pop and save as many addresses as there are // up to a null part, but for now, just save one... _replyTo = msg.Unwrap(); // .split process message // Here is where we actually have a message to process; we // return it to the caller application: return(msg.Duplicate()); } else if (command.StrHexEq(MdpCommon.MdpwCmd.HEARTBEAT)) { // Do nothing for heartbeats } else if (command.StrHexEq(MdpCommon.MdpwCmd.DISCONNECT)) { ConnectToBroker(); } else { "E: invalid input message: '{0}'".DumpString(command.ToString()); } } } } else if (Equals(error, ZError.ETERM)) { cancellor.Cancel(); break; // Interrupted } else if (Equals(error, ZError.EAGAIN) && --Liveness == 0) { if (Verbose) { "W: disconnected from broker - retrying...".DumpString(); } Thread.Sleep(Reconnect); ConnectToBroker(); } // Send HEARTBEAT if it's time if (DateTime.UtcNow > HeartbeatAt) { SendToBroker(MdpCommon.MdpwCmd.HEARTBEAT.ToHexString(), null, null); HeartbeatAt = DateTime.UtcNow + Heartbeat; } } if (cancellor.IsCancellationRequested) { "W: interrupt received, killing worker...\n".DumpString(); } return(null); }
public static bool StrHexEq(this ZFrame zfrm, MdpCommon.MdpwCmd cmd) { return(zfrm.ToString().ToMdCmd().Equals(cmd)); }
// .split main task // Finally, here is the main task. We create a new broker instance and // then process messages on the broker Socket: public static void MDBroker(string[] args) { CancellationTokenSource cancellor = new CancellationTokenSource(); Console.CancelKeyPress += (s, ea) => { ea.Cancel = true; cancellor.Cancel(); }; using (Broker broker = new Broker(Verbose)) { broker.Bind("tcp://*:5555"); // Get and process messages forever or until interrupted while (true) { if (cancellor.IsCancellationRequested || (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) { broker.ShutdownContext(); } var p = ZPollItem.CreateReceiver(); ZMessage msg; ZError error; if (broker.Socket.PollIn(p, out msg, out error, MdpCommon.HEARTBEAT_INTERVAL)) { if (Verbose) { msg.DumpZmsg("I: received message:"); } using (ZFrame sender = msg.Pop()) using (ZFrame empty = msg.Pop()) using (ZFrame header = msg.Pop()) { if (header.ToString().Equals(MdpCommon.MDPC_CLIENT)) { broker.ClientMsg(sender, msg); } else if (header.ToString().Equals(MdpCommon.MDPW_WORKER)) { broker.WorkerMsg(sender, msg); } else { msg.DumpZmsg("E: invalid message:"); msg.Dispose(); } } } else { if (Equals(error, ZError.ETERM)) { "W: interrupt received, shutting down...".DumpString(); break; // Interrupted } if (!Equals(error, ZError.EAGAIN)) { throw new ZException(error); } } // Disconnect and delete any expired workers // Send heartbeats to idle workes if needed if (DateTime.UtcNow > broker.HeartbeatAt) { broker.Purge(); foreach (var waitingworker in broker.Waiting) { waitingworker.Send(MdpCommon.MdpwCmd.HEARTBEAT.ToHexString(), null, null); } broker.HeartbeatAt = DateTime.UtcNow + MdpCommon.HEARTBEAT_INTERVAL; } } } }
// .split broker worker_msg method // This method processes one READY, REPLY, HEARTBEAT, or // DISCONNECT message sent to the broker by a worker: public void WorkerMsg(ZFrame sender, ZMessage msg) { if (msg.Count < 1) // At least, command { throw new InvalidOperationException(); } ZFrame command = msg.Pop(); //string id_string = sender.ReadString(); bool isWorkerReady; //string id_string; using (var sfrm = sender.Duplicate()) { var idString = sfrm.Read().ToHexString(); isWorkerReady = Workers.ContainsKey(idString); } Worker worker = RequireWorker(sender); using (msg) using (command) { if (command.StrHexEq(MdpCommon.MdpwCmd.READY)) { if (isWorkerReady) { // Not first command in session worker.Delete(true); } else if (command.Length >= 4 && command.ToString().StartsWith("mmi.")) { // Reserd servicee name worker.Delete(true); } else { // Attach worker to service and mark as idle using (ZFrame serviceFrame = msg.Pop()) { worker.Service = RequireService(serviceFrame); worker.Service.Workers++; worker.Waiting(); } } } else if (command.StrHexEq(MdpCommon.MdpwCmd.REPLY)) { if (isWorkerReady) { // Remove and save client return envelope and insert the // protocol header and service name, then rewrap envelope. ZFrame client = msg.Unwrap(); msg.Prepend(new ZFrame(worker.Service.Name)); msg.Prepend(new ZFrame(MdpCommon.MDPC_CLIENT)); msg.Wrap(client); Socket.Send(msg); worker.Waiting(); } else { worker.Delete(true); } } else if (command.StrHexEq(MdpCommon.MdpwCmd.HEARTBEAT)) { if (isWorkerReady) { worker.Expiry = DateTime.UtcNow + MdpCommon.HEARTBEAT_EXPIRY; } else { worker.Delete(true); } } else if (command.StrHexEq(MdpCommon.MdpwCmd.DISCONNECT)) { worker.Delete(false); } else { msg.DumpZmsg("E: invalid input message"); } } }
private void UpdateLoop(object sender, DoWorkEventArgs e) { loopActive = true; int loopInterval = 50; // Loop to get and assign elements, then update UI while (loopActive) { AsyncFormUpdate(new Action(() => { lblStatus.Text = "Update Loop Running"; })); // Check to see if thread cancellation is pending // If true, stop the loop if (bwUpdateLoop.CancellationPending) { requester.Close(); context.Shutdown(); context.Dispose(); requester.Dispose(); loopActive = false; AsyncFormUpdate(new Action(() => { lblStatus.Text = "Update Loop Stopped"; })); break; } /// Socket Code // Send requester.Send(new ZFrame(_autoMode)); try { // Receive using (ZFrame reply = requester.ReceiveFrame()) { returnedData = JObject.Parse(reply.ToString()); } } catch (ZException ex) { requester.Close(); context.Shutdown(); context.Dispose(); requester.Dispose(); loopActive = false; AsyncFormUpdate(new Action(() => { lblMode.Text = "Loop Stopped"; lblStatus.Text = "Update Loop Stopped"; })); break; } // ====================================================== // Take all elements from the Dictionary and update the UI with them AsyncFormUpdate(new Action(() => ShowUI(returnedData))); // Stall the thread for a given interval to save on bandwidth Thread.Sleep(loopInterval); } /// update the UI with this status AsyncFormUpdate(new Action(() => { lblMode.Text = "Not Connected"; })); }