/// <summary> /// Create a Zyre API that communicates with a node on the ZRE bus. /// </summary> /// <param name="name">The name of the node</param> /// <param name="useEvents">Set this to true to disable Receive() and instead subscribe to events for getting messages from peers. Default is true.</param> /// <param name="loggerDelegate">An action to take for logging when _verbose is true. Default is null.</param> public Zyre (string name, bool useEvents = true, Action<string> loggerDelegate = null) { _useEvents = useEvents; // Create front-to-back pipe pair for data traffic // outbox is passed to ZyreNode for sending Zyre message traffic back to _inbox PairSocket outbox; PairSocket.CreateSocketPair(out outbox, out _inbox); // Start node engine and wait for it to be ready // All node control is done through _actor _actor = ZyreNode.Create(outbox, loggerDelegate); if (useEvents) { _inboxPoller = new NetMQPoller(); _inbox.ReceiveReady += InboxReceiveReady; _inboxPoller.RunAsync(); } // Send name, if any, to node ending if (!string.IsNullOrEmpty(name)) { _actor.SendMoreFrame("SET NAME").SendFrame(name); } }
/// <summary> /// Subscribe to Nyx group /// </summary> /// <param name="topic"></param> /// <returns></returns> public void AddToGroup(string topic) { try { if (string.IsNullOrWhiteSpace(topic)) { _logger.Warn("Topic is empty."); return; } if (_subscribersChannels.Contains(topic)) { _logger.Warn($"Topic {topic} already registered."); return; } if (_subscribersChannels.Add(topic)) { _actor?.SendMoreFrame("subscribe").SendFrame(topic); } } catch (Exception ex) { _logger.Error($"Error adding subscription {topic}.", ex); } }
public void Publish(NetMQMessage message) { // we can use actor like NetMQSocket _actor.SendMoreFrame(PublishMessageCommand).SendMultipartMessage(message); }
public void Subscribe(string topic) { _actor.SendMoreFrame(SubscribeCommand).SendFrame(topic); }
public void Publish(NetMQMessage message) { _actor.SendMoreFrame(PublishMessageCommand).SendMultipartMessage(message); }
/// <summary> /// Default connect /// </summary> /// <param name="ipAddress"></param> /// <param name="port"></param> /// <returns></returns> public bool Connect(string ipAddress = "", int port = 4015) { if (!IsStarted) { _logger.Error("Connection failed. Server not running."); if (_autostart) { _logger.Info("AutoStart is on. Server starting."); if (!Start().Select(x => true).Amb(Observable.Return(false).Delay(TimeSpan.FromSeconds(5))).Wait()) { return(false); } } else { return(false); } } try { if (_isConnected && _lastHubIp == ipAddress) { _logger.Trace("Connection to {0} is active, not reconnecting.", ipAddress); return(true); } if (string.IsNullOrWhiteSpace(ipAddress)) { ipAddress = "127.0.0.1"; } Heartbeat.Instance.Disconnected(); _connectionDisposable?.Dispose(); // Stop previous actor is any. try { _actor?.SendFrame(NetMQActor.EndShimMessage); //_actor?.Dispose(); } catch (Exception ex) { _logger.Error("NetMQ error.", ex); } _actor = null; // Store port and hub address _port = port; _lastHubIp = ipAddress; Heartbeat.Instance.Setup(_lastHubIp, _port); // Create a new actor to handle the comunications from hub. _actor = NetMQActor.Create(BorgShimHandler.Create(_nyxMessageSubject, ipAddress, port)); Heartbeat.Instance.Connected(); CreateServerSocket(); _config.Set("borg", "hubIp", _lastHubIp); // Global channel AddToGroup("global"); // Custom channels SubscribersChannels.ForEach(c => _actor.SendMoreFrame("subscribe").SendFrame(c)); _actor.SendMoreFrame("subscribe").SendFrame(NodeId); _isConnected = true; _connectionDisposable = Disposable.Create(() => _isConnected = false); NyxMessage.Create(BasicHubAction.NyxId, BasicHubAction.Register, NodeId).Set("nodeID", NodeId).SendMessage(this); } catch (Exception ex) { _logger.Error("Error connecting...", ex); return(false); } return(true); }
/// <summary> /// Set Name (the public name of this node) /// </summary> /// <param name="name">the name to set</param> public void SetName(string name) { _actor.SendMoreFrame("SET NAME").SendFrame(name); }
/// <summary> /// Here we handle messages coming from other peers /// </summary> private void ReceivePeer() { Guid uuid; var msg = ZreMsg.ReceiveNew(_inbox, out uuid); if (msg == null) { // Ignore a bad message (header or message signature doesn't meet http://rfc.zeromq.org/spec:36) _loggerDelegate?.Invoke("Ignoring a bad message (header or message signature doesn't meet http://rfc.zeromq.org/spec:36)."); return; } if (uuid == _uuid) { var text = $"({_name}) Our own message should not be coming back to us! {_uuid}"; _loggerDelegate?.Invoke(text); throw new InvalidOperationException(text); } _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(ReceivePeer)}() received message={msg}"); Debug.Assert(uuid != _uuid, $"({_name}) Our own message should not be coming back to us! uuid={_uuid}"); ZyrePeer peer; if (!_peers.TryGetValue(uuid, out peer)) { _loggerDelegate?.Invoke($"Peer {uuid.ToShortString6()} is unknown."); } if (msg.Id == ZreMsg.MessageId.Hello) { // On HELLO we may create the peer if it's unknown // On other commands the peer must already exist if (peer != null) { // Remove fake peers if (peer.Ready) { _loggerDelegate?.Invoke("Removing fake peer={peer} because we received another HELLO from the same uuid."); RemovePeer(peer); Debug.Assert(!_peers.ContainsKey(uuid)); } else if (peer.Endpoint == _endpoint) { // We ignore HELLO, if peer has same endpoint as current node _loggerDelegate?.Invoke("Ignoring HELLO for peer that has same endpoint as current node."); return; } } peer = RequirePeer(uuid, msg.Hello.Endpoint); //_loggerDelegate?.Invoke($"TMP Did {nameof(RequirePeer)}"); peer.Ready = true; } if (peer == null) { _loggerDelegate?.Invoke("Ignoring null peer"); return; } if (!peer.Ready) { // Ignore command if peer isn't ready _loggerDelegate?.Invoke($"Ignoring peer that isn't ready: {peer}"); return; } if (peer.MessagesLost(msg)) { _loggerDelegate?.Invoke($"MessagesLost! {nameof(ZyreNode)}.{nameof(ReceivePeer)}() ignoring message={msg} and removing peer={peer} "); RemovePeer(peer); return; } // Now process each command _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(ReceivePeer)}() is now ready to process this {msg.Id} command."); switch (msg.Id) { case ZreMsg.MessageId.Hello: // Store properties from HELLO command into peer var helloMessage = msg.Hello; peer.Name = helloMessage.Name; peer.Headers = helloMessage.Headers; // Tell the caller about the peer var headersBuffer = Serialization.BinarySerialize(peer.Headers); _outbox.SendMoreFrame("ENTER").SendMoreFrame(peer.Uuid.ToByteArray()).SendMoreFrame(peer.Name).SendMoreFrame(headersBuffer).SendFrame(helloMessage.Endpoint); _loggerDelegate?.Invoke($"ENTER name={peer.Name} endpoint={peer.Endpoint}"); // Join peer to listed groups foreach (var groupName in helloMessage.Groups) { JoinPeerGroup(peer, groupName); } // Now take peer's status from HELLO, after joining groups peer.Status = helloMessage.Status; _loggerDelegate?.Invoke($"Hello message has been processed for peer: {peer}"); break; case ZreMsg.MessageId.Whisper: // Pass up to caller API as WHISPER event _outbox.SendMoreFrame("WHISPER").SendMoreFrame(uuid.ToByteArray()).SendMoreFrame(peer.Name).SendMultipartMessage(msg.Whisper.Content); break; case ZreMsg.MessageId.Shout: // Pass up to caller API as SHOUT event _outbox.SendMoreFrame("SHOUT").SendMoreFrame(uuid.ToByteArray()).SendMoreFrame(peer.Name).SendMoreFrame(msg.Shout.Group).SendMultipartMessage(msg.Shout.Content); break; case ZreMsg.MessageId.Join: JoinPeerGroup(peer, msg.Join.Group); Debug.Assert(msg.Join.Status == peer.Status); break; case ZreMsg.MessageId.Leave: LeavePeerGroup(peer, msg.Leave.Group); Debug.Assert(msg.Leave.Status == peer.Status); break; case ZreMsg.MessageId.Ping: // Respond to a PING request with PING-OK, always _actor.SendMoreFrame("PING-OK").SendFrame(uuid.ToByteArray()); // Notify Zyre application PING came in _outbox.SendMoreFrame("PING").SendMoreFrame(uuid.ToByteArray()).SendFrame(peer.Name); break; case ZreMsg.MessageId.PingOk: // Good, the peer is alive, we will reset our timers // with the peer.Refresh(); call below. _outbox.SendMoreFrame("PING-OK").SendMoreFrame(uuid.ToByteArray()).SendFrame(peer.Name); break; default: throw new ArgumentOutOfRangeException(); } // Activity from peer resets peer timers peer.Refresh(); //_loggerDelegate?.Invoke($"TMP Leaving {nameof(ReceivePeer)}"); }
public Zyre (string name) { // Create front-to-back pipe pair for data traffic // outbox is passed to ZreNode for sending Zyre message traffic back to _inbox PairSocket outbox; PairSocket.CreateSocketPair(out outbox, out _inbox); // Start node engine and wait for it to be ready // All node control is done through _actor _actor = ZreNode.Create(outbox); // Send name, if any, to node ending if (!string.IsNullOrEmpty(name)) { _actor.SendMoreFrame("SET NAME").SendFrame(name); } }