/// <summary> /// We do this once a second: /// - if peer has gone quiet, send TCP ping and emit EVASIVE event /// - if peer has disappeared, expire it /// </summary> /// <param name="peer">the peer to ping</param> /// <returns>true if this peer should be removed</returns> private bool PingPeer(ZyrePeer peer) { if (!_isRunning) { // We have been stopped. We know longer can communicate to peers. return(true); } if (DateTime.Now >= peer.ExpiredAt) { return(true); } if (DateTime.Now >= peer.EvasiveAt) { // If peer is being evasive, force a TCP ping. // ZeroMQTODO: do this only once for a peer in this state; // it would be nicer to use a proper state machine // for peer management. _loggerDelegate?.Invoke($"Peer seems dead/slow: name={peer.Name} endpoint={peer.Endpoint}"); ZreMsg.SendPing(_outbox, 0); // Inform the calling application this peer is being evasive _outbox.SendMoreFrame("EVASIVE"); _outbox.SendMoreFrame(peer.Uuid.ToByteArray()); _outbox.SendFrame(peer.Name); } return(false); }
internal void Read(ZreMsg m) { int listSize; int hashSize; int chunkSize; byte[] guidBytes; byte version; // Version version = m.GetNumber1(); if (version != 2) { throw new MessageException("Version is invalid"); } // Sequence Sequence = m.GetNumber2(); // Group Group = m.GetString(); // Status Status = m.GetNumber1(); }
/// <summary> /// Send message to all peers in group /// </summary> /// <param name="msg">the message</param> internal void Send(ZreMsg msg) { foreach (var peer in _peers.Values) { peer.Send(msg); } }
/// <summary> /// Send message to all peers in group /// </summary> /// <param name="msg"></param> public void Send(ZreMsg msg) { foreach (var peer in m_peers.Values) { peer.Send(msg); } }
internal void Write(ZreMsg m) { // Version m.PutNumber1(2); // Version // Sequence m.PutNumber2(Sequence); }
/// <summary> /// Send message to peer /// </summary> /// <param name="msg">the message</param> /// <returns>always true</returns> public bool Send(ZreMsg msg) { if (m_connected) { msg.Sequence = m_sentSequence++; msg.Send(m_mailbox); } return(true); }
internal void Write(ZreMsg m) { // Version m.PutNumber1(2); // Version // Sequence m.PutNumber2(Sequence); // Group m.PutString(Group); // Content }
/// <summary> /// Send a PingOk message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> public static void SendPingOk(IOutgoingSocket socket, ushort sequence) { var msg = new ZreMsg { Id = MessageId.PingOk, PingOk = { Version = 2, Sequence = sequence, } }; msg.Send(socket); }
/// <summary> /// Send a Ping message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <returns>true if message was successfully sent</returns> public static bool SendPing(IOutgoingSocket socket, ushort sequence) { var msg = new ZreMsg { Id = MessageId.Ping, Ping = { Version = 2, Sequence = sequence, } }; return(msg.Send(socket)); }
/// <summary> /// Check if messages were lost from peer, returns true if they were /// </summary> /// <param name="msg"></param> /// <returns>true if we have lost one or more message</returns> public bool MessagesLost(ZreMsg msg) { Debug.Assert(msg != null); if (msg.Id == ZreMsg.MessageId.Hello) { // HELLO always MUST have sequence = 1 m_wantSequence = 1; } else { m_wantSequence = m_wantSequence == UshortMax ? (ushort)0 : m_wantSequence++; } return(m_wantSequence != msg.Sequence); }
/// <summary> /// Send message to peer /// </summary> /// <param name="msg">the message</param> /// <returns>always true</returns> internal bool Send(ZreMsg msg) { if (Connected) { msg.Sequence = ++_sentSequence; _loggerDelegate?.Invoke($"{nameof(ZyrePeer)}.{nameof(Send)}() sending message={msg} to Endpoint={Endpoint}"); var success = msg.Send(_mailbox); if (!success) { _loggerDelegate?.Invoke($"{nameof(ZyrePeer)}.{nameof(Send)}() UNABLE to send message={msg} to Endpoint={Endpoint}"); } } return(true); }
/// <summary> /// Send a Whisper message to the socket /// Warning re WHISPER and SHOUT: The 0MQ spec http://rfc.zeromq.org/spec:36 /// says "message content defined as one 0MQ frame. ZRE does not support multi-frame message contents." /// ...on the other hand, it appears that zeromq/zyre also supports multi-frame contents, as per the top of zyre.c /// This C# implementation allows multi-frame contents. /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="content">See warning above</param> /// <returns>true if message was successfully sent</returns> public static bool SendWhisper(IOutgoingSocket socket, ushort sequence, NetMQMessage content) { var msg = new ZreMsg { Id = MessageId.Whisper, Whisper = { Version = 2, Sequence = sequence, Content = content } }; return(msg.Send(socket)); }
/// <summary> /// Send a Shout message to the socket /// Warning re WHISPER and SHOUT: The 0MQ spec http://rfc.zeromq.org/spec:36 /// says "message content defined as one 0MQ frame. ZRE does not support multi-frame message contents." /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="content">See warning above</param> public static void SendShout(IOutgoingSocket socket, ushort sequence, NetMQMessage content) { var msg = new ZreMsg { Id = MessageId.Shout, Shout = { Version = 2, Sequence = sequence, Content = content } }; msg.Send(socket); }
/// <summary> /// Find or create peer via its UUID /// </summary> /// <param name="uuid">the identity of peer</param> /// <param name="endpoint">the endpoint to which we will connect the new peer</param> /// <returns>A peer (existing, or new one connected to endpoint)</returns> private ZyrePeer RequirePeer(Guid uuid, string endpoint) { Debug.Assert(!string.IsNullOrEmpty(endpoint)); ZyrePeer peer; if (_peers.TryGetValue(uuid, out peer)) { if (!_reportedKnownPeersTmp.Contains(peer)) { //var callingMethod1 = MethodNameLevelAbove(); //_loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() called by {callingMethod1} returning already-known peer={peer}"); _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() returning already-known peer={peer}"); _reportedKnownPeersTmp.Add(peer); } return(peer); } // Purge any previous peer on same endpoint foreach (var existingPeer in _peers.Values) { PurgePeer(existingPeer, endpoint); } //var callingMethod2 = MethodNameLevelAbove(); //_loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() called by {callingMethod2} creating new peer with uuidShort={uuid.ToShortString6()}"); _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() creating new peer with uuidShort={uuid.ToShortString6()}"); peer = ZyrePeer.NewPeer(_peers, uuid, _loggerDelegate); peer.Origin = _name; peer.Connect(_uuid, endpoint); // Handshake discovery by sending HELLO as first message var helloMessage = new ZreMsg { Id = ZreMsg.MessageId.Hello, Hello = { Endpoint = _endpoint, Groups = _ownGroups.Keys.ToList(), Status = _status, Name = _name, Headers = _headers } }; //_loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() called by {callingMethod2} created new peer={peer}. Sending Hello message..."); _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() created new peer={peer}. Sending Hello message..."); peer.Send(helloMessage); return(peer); }
/// <summary> /// Send a Join message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="group"></param> /// <param name="status"></param> /// <returns>true if message was successfully sent</returns> public static bool SendJoin(IOutgoingSocket socket, ushort sequence, string group, byte status) { var msg = new ZreMsg { Id = MessageId.Join, Join = { Version = 2, Sequence = sequence, Group = group, Status = status, } }; return(msg.Send(socket)); }
/// <summary> /// Send a Leave message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="group"></param> /// <param name="status"></param> public static void SendLeave(IOutgoingSocket socket, ushort sequence, string group, byte status) { var msg = new ZreMsg { Id = MessageId.Leave, Leave = { Version = 2, Sequence = sequence, Group = group, Status = status, } }; msg.Send(socket); }
internal void Read(ZreMsg m) { int listSize; int hashSize; int chunkSize; byte[] guidBytes; byte version; // Version version = m.GetNumber1(); if (version != 2) { throw new MessageException("Version is invalid"); } // Sequence Sequence = m.GetNumber2(); // Endpoint Endpoint = m.GetString(); // Groups listSize = (int)m.GetNumber4(); Groups = new List <string>(listSize); while (listSize-- > 0) { string s = m.GetLongString(); Groups.Add(s); } // Status Status = m.GetNumber1(); // Name Name = m.GetString(); // Headers hashSize = (int)m.GetNumber4(); Headers = new Dictionary <string, string>(); while (hashSize-- > 0) { string key = m.GetString(); string value = m.GetLongString(); Headers.Add(key, value); } }
internal void Write(ZreMsg m) { // Version m.PutNumber1(2); // Version // Sequence m.PutNumber2(Sequence); // Endpoint m.PutString(Endpoint); // Groups if (Groups != null) { m.PutNumber4((UInt32)Groups.Count); foreach (string s in Groups) { m.PutLongString(s); } } else { m.PutNumber4(0); // Empty string array } // Status m.PutNumber1(Status); // Name m.PutString(Name); // Headers if (Headers != null) { m.PutNumber4((UInt32)Headers.Count); foreach (var pair in Headers) { m.PutString(pair.Key); m.PutLongString(pair.Value); } } else { m.PutNumber4(0); // Empty dictionary } }
/// <summary> /// Return a new ZreMsg based received from the input socket. /// Message is ignored if input is a RouterSocket and the message header doesn't meet the http://rfc.zeromq.org/spec:36 spec. /// Message is ignored if the message signature doesn't start with %xAA %xA1. /// </summary> /// <param name="input">the socket</param> /// <param name="uuid">The identity Guid received into the RoutingId, or Guid.Empty</param> /// <returns>null if not a valid message (message to be ignored)</returns> public static ZreMsg ReceiveNew(RouterSocket input, out Guid uuid) { var msg = new ZreMsg(); if (msg.Receive(input)) { if (msg.RoutingId.Length != 17 || msg.RoutingId[0] != 0x1) { uuid = Guid.Empty; return(null); } uuid = GetUuid(msg.RoutingId, 1); return(msg); } uuid = Guid.Empty; return(null); }
/// <summary> /// Send a Hello message to the socket /// </summary> /// <param name="socket"></param> /// <param name="endpoint"></param> /// <param name="groups"></param> /// <param name="status"></param> /// <param name="name"></param> /// <param name="headers"></param> /// <returns>true if message was successfully sent</returns> public static bool SendHello(IOutgoingSocket socket, string endpoint, List <string> groups, byte status, string name, Dictionary <string, string> headers) { var msg = new ZreMsg { Id = MessageId.Hello, Hello = { Endpoint = endpoint, Groups = groups, Status = status, Name = name, Headers = headers } }; return(msg.Send(socket)); }
/// <summary> /// Send a Hello message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="endpoint"></param> /// <param name="groups"></param> /// <param name="status"></param> /// <param name="name"></param> /// <param name="headers"></param> public static void SendHello(IOutgoingSocket socket, ushort sequence, string endpoint, List <string> groups, byte status, string name, Dictionary <string, string> headers) { var msg = new ZreMsg { Id = MessageId.Hello, Hello = { Version = 2, Sequence = sequence, Endpoint = endpoint, Groups = groups, Status = status, Name = name, Headers = headers } }; msg.Send(socket); }
/// <summary> /// Check if messages were lost from peer, returns true if they were /// </summary> /// <param name="msg"></param> /// <returns>true if we have lost one or more message</returns> internal bool MessagesLost(ZreMsg msg) { Debug.Assert(msg != null); // The sequence number set by the peer and our own calculated sequence number should be the same. if (msg.Id == ZreMsg.MessageId.Hello) { // HELLO always MUST have sequence = 1 _wantSequence = 1; } else { _wantSequence = _wantSequence == UshortMax ? (ushort)0 : ++_wantSequence; } if (_wantSequence != msg.Sequence) { _loggerDelegate?.Invoke($"Sequence error for peer={Name} expected={_wantSequence}, got={msg.Sequence}"); return(true); } return(false); }
public void WhisperTest() { Action<ZreMsg> setMessage = m => { m.Id = ZreMsg.MessageId.Whisper; m.Whisper.Sequence = 123; }; Action<ZreMsg> checkMessage = m=> { Assert.That(m.Id, Is.EqualTo(ZreMsg.MessageId.Whisper)); Assert.That(m.Whisper.Sequence, Is.EqualTo(123)); Assert.That(m.Whisper.Content.FrameCount, Is.EqualTo(1)); }; using (NetMQContext context = NetMQContext.Create()) using (var client = context.CreateDealerSocket()) using (var server = context.CreateRouterSocket()) { server.Bind("inproc://zprototest"); client.Connect("inproc://zprototest"); ZreMsg clientMessage = new ZreMsg(); ZreMsg serverMessage = new ZreMsg(); for (int i=0; i < 2; i++) { // client send message to server setMessage(clientMessage); clientMessage.Send(client); // server receive the message serverMessage.Receive(server); // check that message received ok Assert.That(serverMessage.RoutingId, Is.Not.Null); checkMessage(serverMessage); // reply to client, no need to set the message, using client data serverMessage.Send(server); // client receive the message clientMessage.Receive(client); // check that message received ok Assert.That(clientMessage.RoutingId, Is.Null); checkMessage(clientMessage); } } }
/// <summary> /// Send message to peer /// </summary> /// <param name="msg">the message</param> /// <returns>always true</returns> public bool Send(ZreMsg msg) { if (_connected) { msg.Sequence = _sentSequence++; msg.Send(_mailbox); } return true; }
/// <summary> /// Here we handle the different control messages from the front-end /// </summary> private void ReceiveApi() { // Get the whole message off the pipe in one go var request = _pipe.ReceiveMultipartMessage(); var command = request.Pop().ConvertToString(); switch (command) { case "UUID": _pipe.SendFrame(_uuid.ToByteArray()); break; case "NAME": _pipe.SendFrame(_name); break; case "ENDPOINT": _pipe.SendFrame(_endpoint ?? ""); break; case "SET NAME": _name = request.Pop().ConvertToString(); Debug.Assert(!String.IsNullOrEmpty(_name)); break; case "SET HEADER": var key = request.Pop().ConvertToString(); var value = request.Pop().ConvertToString(); _headers[key] = value; break; case "SET PORT": var str = request.Pop().ConvertToString(); Int32.TryParse(str, out _beaconPort); break; case "SET INTERVAL": var intervalStr = request.Pop().ConvertToString(); TimeSpan.TryParse(intervalStr, out _interval); break; case "START": var interfaceName = request.Pop().ConvertToString(); Start(interfaceName); break; case "STOP": Stop(); break; case "WHISPER": // Get peer to send message to var uuid = PopGuid(request); ZyrePeer peer; if (_peers.TryGetValue(uuid, out peer)) { // Send frame on out to peer's mailbox, drop message // if peer doesn't exist (may have been destroyed) var msg = new ZreMsg { Id = ZreMsg.MessageId.Whisper, Whisper = { Content = request } }; peer.Send(msg); } break; case "SHOUT": // Get group to send message to var groupNameShout = request.Pop().ConvertToString(); ZyreGroup group; if (_peerGroups.TryGetValue(groupNameShout, out group)) { var msg = new ZreMsg { Id = ZreMsg.MessageId.Shout, Shout = { Group = groupNameShout, Content = request } }; group.Send(msg); } break; case "JOIN": var groupNameJoin = request.Pop().ConvertToString(); ZyreGroup groupJoin; if (!_ownGroups.TryGetValue(groupNameJoin, out groupJoin)) { // Only send if we're not already in group ZyreGroup.NewGroup(groupNameJoin, _ownGroups); // Update status before sending command _status = _status == UbyteMax ? (byte)0 : ++_status; var msg = new ZreMsg { Id = ZreMsg.MessageId.Join, Join = { Group = groupNameJoin, Status = _status } }; foreach (var peerJoin in _peers.Values) { peerJoin.Send(msg); } } break; case "LEAVE": var groupNameLeave = request.Pop().ConvertToString(); ZyreGroup groupLeave; if (_ownGroups.TryGetValue(groupNameLeave, out groupLeave)) { // Only send if we are actually in group // Update status before sending command _status = _status == UbyteMax ? (byte)0 : ++_status; var msg = new ZreMsg { Id = ZreMsg.MessageId.Leave, Leave = { Group = groupNameLeave, Status = _status } }; foreach (var peerLeave in _peers.Values) { peerLeave.Send(msg); } _ownGroups.Remove(groupNameLeave); } break; case "PEERS": // Send the list of the _peers keys var peersKeyBuffer = Serialization.BinarySerialize(_peers.Keys.ToList()); _pipe.SendFrame(peersKeyBuffer); break; case "PEER ENDPOINT": var uuidForEndpoint = PopGuid(request); var peerForEndpoint = _peers[uuidForEndpoint]; // throw exception if not found _pipe.SendFrame(peerForEndpoint.Endpoint); break; case "PEER NAME": var uuidForName = PopGuid(request); var peerForName = _peers[uuidForName]; // throw exception if not found _pipe.SendFrame(peerForName.Name); break; case "PEER HEADER": var uuidForHeader = PopGuid(request); var keyForHeader = request.Pop().ConvertToString(); ZyrePeer peerForHeader; if (_peers.TryGetValue(uuidForHeader, out peerForHeader)) { string header; if (peerForHeader.Headers.TryGetValue(keyForHeader, out header)) { _pipe.SendFrame(header); } else { _loggerDelegate?.Invoke($"No header with key {keyForHeader} in peer uuidShort={uuidForHeader.ToShortString6()}"); _pipe.SendFrame(""); } } else { _loggerDelegate?.Invoke($"PEER HEADER requested for peer uuidShort={uuidForHeader.ToShortString6()} that is not a peer"); _pipe.SendFrame(""); } break; case "PEER GROUPS": // Send a list of the _peerGroups keys, comma-delimited var peerGroupsKeyBuffer = Serialization.BinarySerialize(_peerGroups.Keys.ToList()); _pipe.SendFrame(peerGroupsKeyBuffer); break; case "OWN GROUPS": // Send a list of the _ownGroups keys, comma-delimited var ownGroupsKeyBuffer = Serialization.BinarySerialize(_ownGroups.Keys.ToList()); _pipe.SendFrame(ownGroupsKeyBuffer); break; case "DUMP": Dump(); break; case NetMQActor.EndShimMessage: // API shut us down _poller?.Stop(); break; default: throw new ArgumentException(command); } }
/// <summary> /// Here we handle the different control messages from the front-end /// </summary> public void ReceiveApi() { // Get the whole message off the pipe in one go var request = _pipe.ReceiveMultipartMessage(); var command = request.Pop().ConvertToString(); switch (command) { case "UUID": _pipe.SendFrame(_uuid.ToString()); break; case "NAME": _pipe.SendFrame(_name); break; case "SET NAME": _name = request.Pop().ConvertToString(); Debug.Assert(!string.IsNullOrEmpty(_name)); break; case "SET HEADER": var name = request.Pop().ConvertToString(); var value = request.Pop().ConvertToString(); _headers[name] = value; break; case "SET PORT": var str = request.Pop().ConvertToString(); int.TryParse(str, out _port); break; case "SET INTERVAL": var intervalStr = request.Pop().ConvertToString(); TimeSpan.TryParse(intervalStr, out _interval); break; case "START": Start(); break; case "STOP": Stop(); break; case "WHISPER": // Get peer to send message to var uuid = PopGuid(request); ZrePeer peer; if (_peers.TryGetValue(uuid, out peer)) { // Send frame on out to peer's mailbox, drop message // if peer doesn't exist (may have been destroyed) var msg = new ZreMsg { Id = ZreMsg.MessageId.Whisper, Whisper = {Content = request} }; peer.Send(msg); } break; case "SHOUT": // Get group to send message to var groupName = request.Pop().ConvertToString(); ZreGroup group; if (_ownGroups.TryGetValue(groupName, out group)) { var msg = new ZreMsg { Id = ZreMsg.MessageId.Shout, Shout = {Content = request} }; group.Send(msg); } break; case "JOIN": var groupNameJoin = request.Pop().ConvertToString(); ZreGroup groupJoin; if (!_ownGroups.TryGetValue(groupNameJoin, out groupJoin)) { // Only send if we're not already in group var msg = new ZreMsg { Id = ZreMsg.MessageId.Join, Join = {Group = groupNameJoin} }; // Update status before sending command IncrementStatus(); foreach (var peerJoin in _peers.Values) { peerJoin.Send(msg); } } break; case "LEAVE": var groupNameLeave = request.Pop().ConvertToString(); ZreGroup groupLeave; if (_ownGroups.TryGetValue(groupNameLeave, out groupLeave)) { // Only send if we are actually in group var msg = new ZreMsg { Id = ZreMsg.MessageId.Leave, Join = {Group = groupNameLeave} }; // Update status before sending command IncrementStatus(); foreach (var peerLeave in _peers.Values) { peerLeave.Send(msg); } _ownGroups.Remove(groupNameLeave); } break; case "PEERS": // Send the list of the _peers keys var peersKeyBuffer = Serialization.BinarySerialize(_peers.Keys.ToList()); _pipe.SendFrame(peersKeyBuffer); break; case "PEER ENDPOINT": var uuidForEndpoint = PopGuid(request); var peerForEndpoint = _peers[uuidForEndpoint]; // throw exception if not found _pipe.SendFrame(peerForEndpoint.Endpoint); break; case "PEER NAME": var uuidForName = PopGuid(request); var peerForName = _peers[uuidForName]; // throw exception if not found _pipe.SendFrame(peerForName.Name); break; case "PEER HEADER": var uuidForHeader = PopGuid(request); var keyForHeader = request.Pop().ConvertToString(); ZrePeer peerForHeader; if (_peers.TryGetValue(uuidForHeader, out peerForHeader)) { string header; _headers.TryGetValue(keyForHeader, out header); _pipe.SendFrame(header ?? ""); } else { _pipe.SendFrame(""); } break; case "PEER GROUPS": // Send a list of the _peerGroups keys, comma-delimited var peerGroupsKeyBuffer = Serialization.BinarySerialize(_peerGroups.Keys.ToList()); _pipe.SendFrame(peerGroupsKeyBuffer); break; case "OWN GROUPS": // Send a list of the _ownGroups keys, comma-delimited var ownGroupsKeyBuffer = Serialization.BinarySerialize(_ownGroups.Keys.ToList()); _pipe.SendFrame(ownGroupsKeyBuffer); break; case NetMQActor.EndShimMessage: _terminated = true; if (_poller != null) { _poller.Stop(); } break; default: throw new ArgumentException(command); } }
internal void Read(ZreMsg m) { int listSize; int hashSize; int chunkSize; byte[] guidBytes; byte version; // Version version = m.GetNumber1(); if (version != 2) { throw new MessageException("Version is invalid"); } // Sequence Sequence = m.GetNumber2(); }
/// <summary> /// Here we handle the different control messages from the front-end /// </summary> private void ReceiveApi() { // Get the whole message off the pipe in one go var request = _pipe.ReceiveMultipartMessage(); var command = request.Pop().ConvertToString(); switch (command) { case "UUID": _pipe.SendFrame(_uuid.ToByteArray()); break; case "NAME": _pipe.SendFrame(_name); break; case "ENDPOINT": _pipe.SendFrame(_endpoint ?? ""); break; case "SET NAME": _name = request.Pop().ConvertToString(); Debug.Assert(!String.IsNullOrEmpty(_name)); break; case "SET HEADER": var key = request.Pop().ConvertToString(); var value = request.Pop().ConvertToString(); _headers[key] = value; break; case "SET PORT": var str = request.Pop().ConvertToString(); Int32.TryParse(str, out _beaconPort); break; case "SET INTERVAL": var intervalStr = request.Pop().ConvertToString(); TimeSpan.TryParse(intervalStr, out _interval); break; case "START": var interfaceName = request.Pop().ConvertToString(); Start(interfaceName); break; case "STOP": Stop(); break; case "WHISPER": // Get peer to send message to var uuid = PopGuid(request); ZyrePeer peer; if (_peers.TryGetValue(uuid, out peer)) { // Send frame on out to peer's mailbox, drop message // if peer doesn't exist (may have been destroyed) var msg = new ZreMsg { Id = ZreMsg.MessageId.Whisper, Whisper = {Content = request} }; peer.Send(msg); } break; case "SHOUT": // Get group to send message to var groupNameShout = request.Pop().ConvertToString(); ZyreGroup group; if (_peerGroups.TryGetValue(groupNameShout, out group)) { var msg = new ZreMsg { Id = ZreMsg.MessageId.Shout, Shout = {Group = groupNameShout, Content = request} }; group.Send(msg); } break; case "JOIN": var groupNameJoin = request.Pop().ConvertToString(); ZyreGroup groupJoin; if (!_ownGroups.TryGetValue(groupNameJoin, out groupJoin)) { // Only send if we're not already in group ZyreGroup.NewGroup(groupNameJoin, _ownGroups); // Update status before sending command _status = _status == UbyteMax ? (byte)0 : ++_status; var msg = new ZreMsg { Id = ZreMsg.MessageId.Join, Join = { Group = groupNameJoin, Status = _status } }; foreach (var peerJoin in _peers.Values) { peerJoin.Send(msg); } } break; case "LEAVE": var groupNameLeave = request.Pop().ConvertToString(); ZyreGroup groupLeave; if (_ownGroups.TryGetValue(groupNameLeave, out groupLeave)) { // Only send if we are actually in group // Update status before sending command _status = _status == UbyteMax ? (byte)0 : ++_status; var msg = new ZreMsg { Id = ZreMsg.MessageId.Leave, Leave = { Group = groupNameLeave, Status = _status } }; foreach (var peerLeave in _peers.Values) { peerLeave.Send(msg); } _ownGroups.Remove(groupNameLeave); } break; case "PEERS": // Send the list of the _peers keys var peersKeyBuffer = Serialization.BinarySerialize(_peers.Keys.ToList()); _pipe.SendFrame(peersKeyBuffer); break; case "PEER ENDPOINT": var uuidForEndpoint = PopGuid(request); var peerForEndpoint = _peers[uuidForEndpoint]; // throw exception if not found _pipe.SendFrame(peerForEndpoint.Endpoint); break; case "PEER NAME": var uuidForName = PopGuid(request); var peerForName = _peers[uuidForName]; // throw exception if not found _pipe.SendFrame(peerForName.Name); break; case "PEER HEADER": var uuidForHeader = PopGuid(request); var keyForHeader = request.Pop().ConvertToString(); ZyrePeer peerForHeader; if (_peers.TryGetValue(uuidForHeader, out peerForHeader)) { string header; if (peerForHeader.Headers.TryGetValue(keyForHeader, out header)) { _pipe.SendFrame(header); } else { _loggerDelegate?.Invoke($"No header with key {keyForHeader} in peer uuidShort={uuidForHeader.ToShortString6()}"); _pipe.SendFrame(""); } } else { _loggerDelegate?.Invoke($"PEER HEADER requested for peer uuidShort={uuidForHeader.ToShortString6()} that is not a peer"); _pipe.SendFrame(""); } break; case "PEER GROUPS": // Send a list of the _peerGroups keys, comma-delimited var peerGroupsKeyBuffer = Serialization.BinarySerialize(_peerGroups.Keys.ToList()); _pipe.SendFrame(peerGroupsKeyBuffer); break; case "OWN GROUPS": // Send a list of the _ownGroups keys, comma-delimited var ownGroupsKeyBuffer = Serialization.BinarySerialize(_ownGroups.Keys.ToList()); _pipe.SendFrame(ownGroupsKeyBuffer); break; case "DUMP": Dump(); break; case NetMQActor.EndShimMessage: // API shut us down _poller?.Stop(); break; default: throw new ArgumentException(command); } }
public void HelloTest() { Action<ZreMsg> setMessage = m => { m.Id = ZreMsg.MessageId.Hello; m.Hello.Sequence = 123; m.Hello.Endpoint = "Life is short but Now lasts for ever"; m.Hello.Groups = new List<string>(); m.Hello.Groups.Add("Name: Brutus"); m.Hello.Groups.Add("Age: 43"); m.Hello.Status = 123; m.Hello.Name = "Life is short but Now lasts for ever"; m.Hello.Headers = new Dictionary<string,string>(); m.Hello.Headers.Add("Name", "Brutus"); m.Hello.Headers.Add("Age", "43"); }; Action<ZreMsg> checkMessage = m=> { Assert.That(m.Id, Is.EqualTo(ZreMsg.MessageId.Hello)); Assert.That(m.Hello.Sequence, Is.EqualTo(123)); Assert.That(m.Hello.Endpoint, Is.EqualTo("Life is short but Now lasts for ever")); Assert.That(m.Hello.Groups.Count, Is.EqualTo(2)); Assert.That(m.Hello.Groups[0], Is.EqualTo("Name: Brutus")); Assert.That(m.Hello.Groups[1], Is.EqualTo("Age: 43")); Assert.That(m.Hello.Status, Is.EqualTo(123)); Assert.That(m.Hello.Name, Is.EqualTo("Life is short but Now lasts for ever")); Assert.That(m.Hello.Headers.Count, Is.EqualTo(2)); Assert.That(m.Hello.Headers["Name"], Is.EqualTo("Brutus")); Assert.That(m.Hello.Headers["Age"], Is.EqualTo("43")); }; using (NetMQContext context = NetMQContext.Create()) using (var client = context.CreateDealerSocket()) using (var server = context.CreateRouterSocket()) { server.Bind("inproc://zprototest"); client.Connect("inproc://zprototest"); ZreMsg clientMessage = new ZreMsg(); ZreMsg serverMessage = new ZreMsg(); for (int i=0; i < 2; i++) { // client send message to server setMessage(clientMessage); clientMessage.Send(client); // server receive the message serverMessage.Receive(server); // check that message received ok Assert.That(serverMessage.RoutingId, Is.Not.Null); checkMessage(serverMessage); // reply to client, no need to set the message, using client data serverMessage.Send(server); // client receive the message clientMessage.Receive(client); // check that message received ok Assert.That(clientMessage.RoutingId, Is.Null); checkMessage(clientMessage); } } }
/// <summary> /// Check if messages were lost from peer, returns true if they were /// </summary> /// <param name="msg"></param> /// <returns>true if we have lost one or more message</returns> public bool MessagesLost(ZreMsg msg) { Debug.Assert(msg != null); if (msg.Id == ZreMsg.MessageId.Hello) { // HELLO always MUST have sequence = 1 _wantSequence = 1; } else { _wantSequence = _wantSequence == UshortMax ? (ushort)0 : _wantSequence++; } return _wantSequence != msg.Sequence; }
internal void Write(ZreMsg m) { // Version m.PutNumber1(2); // Version // Sequence m.PutNumber2(Sequence); // Endpoint m.PutString(Endpoint); // Groups if (Groups != null) { m.PutNumber4((UInt32)Groups.Count); foreach (string s in Groups) { m.PutLongString(s); } } else m.PutNumber4(0); // Empty string array // Status m.PutNumber1(Status); // Name m.PutString(Name); // Headers if (Headers != null) { m.PutNumber4((UInt32)Headers.Count); foreach(var pair in Headers) { m.PutString(pair.Key); m.PutLongString(pair.Value); } } else m.PutNumber4(0); // Empty dictionary }
/// <summary> /// Return a new ZreMsg based received from the input socket. /// Message is ignored if input is a RouterSocket and the message header doesn't meet the http://rfc.zeromq.org/spec:36 spec. /// Message is ignored if the message signature doesn't start with %xAA %xA1. /// </summary> /// <param name="input">the socket</param> /// <param name="uuid">The identity Guid received into the RoutingId, or Guid.Empty</param> /// <returns>null if not a valid message (message to be ignored)</returns> public static ZreMsg ReceiveNew(RouterSocket input, out Guid uuid) { var msg = new ZreMsg(); if (msg.Receive(input)) { if (msg.RoutingId.Length != 17 || msg.RoutingId[0] != 0x1) { uuid = Guid.Empty; return null; } uuid = GetUuid(msg.RoutingId, 1); return msg; } uuid = Guid.Empty; return null; }
/// <summary> /// Send a Shout message to the socket /// Warning re WHISPER and SHOUT: The 0MQ spec http://rfc.zeromq.org/spec:36 /// says "message content defined as one 0MQ frame. ZRE does not support multi-frame message contents." /// But this C# implementation allows multi-frame contents. /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="content">See warning above</param> public static void SendShout(IOutgoingSocket socket, ushort sequence, NetMQMessage content) { var msg = new ZreMsg { Id = MessageId.Shout, Shout = { Version = 2, Sequence = sequence, Content = content } }; msg.Send(socket); }
/// <summary> /// Send message to peer /// </summary> /// <param name="msg">the message</param> /// <returns>always true</returns> internal bool Send(ZreMsg msg) { if (Connected) { msg.Sequence = ++_sentSequence; _loggerDelegate?.Invoke($"{nameof(ZyrePeer)}.{nameof(Send)}() sending message={msg} to Endpoint={Endpoint}"); var success = msg.Send(_mailbox); if (!success) { _loggerDelegate?.Invoke($"{nameof(ZyrePeer)}.{nameof(Send)}() UNABLE to send message={msg} to Endpoint={Endpoint}"); } } return true; }
/// <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: break; case ZreMsg.MessageId.PingOk: Debug.Fail("Unexpected"); break; default: throw new ArgumentOutOfRangeException(); } // Activity from peer resets peer timers peer.Refresh(); //_loggerDelegate?.Invoke($"TMP Leaving {nameof(ReceivePeer)}"); }
internal void Read(ZreMsg m) { int listSize; int hashSize; int chunkSize; byte[] guidBytes; byte version; // Version version = m.GetNumber1(); if (version != 2) { throw new MessageException("Version is invalid"); } // Sequence Sequence = m.GetNumber2(); // Endpoint Endpoint = m.GetString(); // Groups listSize = (int)m.GetNumber4(); Groups = new List<string>(listSize); while (listSize-- > 0) { string s = m.GetLongString(); Groups.Add(s); } // Status Status = m.GetNumber1(); // Name Name = m.GetString(); // Headers hashSize = (int)m.GetNumber4(); Headers = new Dictionary<string, string>(); while (hashSize-- > 0) { string key = m.GetString(); string value = m.GetLongString(); Headers.Add(key, value); } }
public void LeaveTest() { Action<ZreMsg> setMessage = m => { m.Id = ZreMsg.MessageId.Leave; m.Leave.Sequence = 123; m.Leave.Group = "Life is short but Now lasts for ever"; m.Leave.Status = 123; }; Action<ZreMsg> checkMessage = m=> { Assert.That(m.Id, Is.EqualTo(ZreMsg.MessageId.Leave)); Assert.That(m.Leave.Sequence, Is.EqualTo(123)); Assert.That(m.Leave.Group, Is.EqualTo("Life is short but Now lasts for ever")); Assert.That(m.Leave.Status, Is.EqualTo(123)); }; using (NetMQContext context = NetMQContext.Create()) using (var client = context.CreateDealerSocket()) using (var server = context.CreateRouterSocket()) { server.Bind("inproc://zprototest"); client.Connect("inproc://zprototest"); ZreMsg clientMessage = new ZreMsg(); ZreMsg serverMessage = new ZreMsg(); for (int i=0; i < 2; i++) { // client send message to server setMessage(clientMessage); clientMessage.Send(client); // server receive the message serverMessage.Receive(server); // check that message received ok Assert.That(serverMessage.RoutingId, Is.Not.Null); checkMessage(serverMessage); // reply to client, no need to set the message, using client data serverMessage.Send(server); // client receive the message clientMessage.Receive(client); // check that message received ok Assert.That(clientMessage.RoutingId, Is.Null); checkMessage(clientMessage); } } }
internal void Write(ZreMsg m) { // Version m.PutNumber1(2); // Version // Sequence m.PutNumber2(Sequence); // Group m.PutString(Group); // Status m.PutNumber1(Status); }
/// <summary> /// Find or create peer via its UUID /// </summary> /// <param name="uuid">the identity of peer</param> /// <param name="endpoint">the endpoint to which we will connect the new peer</param> /// <returns>A peer (existing, or new one connected to endpoint)</returns> private ZyrePeer RequirePeer(Guid uuid, string endpoint) { Debug.Assert(!string.IsNullOrEmpty(endpoint)); ZyrePeer peer; if (_peers.TryGetValue(uuid, out peer)) { if (!_reportedKnownPeersTmp.Contains(peer)) { var callingMethod1 = MethodNameLevelAbove(); _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() called by {callingMethod1} returning already-known peer={peer}"); _reportedKnownPeersTmp.Add(peer); } return peer; } // Purge any previous peer on same endpoint foreach (var existingPeer in _peers.Values) { PurgePeer(existingPeer, endpoint); } var callingMethod2 = MethodNameLevelAbove(); _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() called by {callingMethod2} creating new peer with uuidShort={uuid.ToShortString6()}"); peer = ZyrePeer.NewPeer(_peers, uuid, _loggerDelegate); peer.Origin = _name; peer.Connect(_uuid, endpoint); // Handshake discovery by sending HELLO as first message var helloMessage = new ZreMsg { Id = ZreMsg.MessageId.Hello, Hello = { Endpoint = _endpoint, Groups = _ownGroups.Keys.ToList(), Status = _status, Name = _name, Headers = _headers } }; _loggerDelegate?.Invoke($"{nameof(ZyreNode)}.{nameof(RequirePeer)}() called by {callingMethod2} created new peer={peer}. Sending Hello message..."); peer.Send(helloMessage); return peer; }
/// <summary> /// Send a Whisper message to the socket /// Warning re WHISPER and SHOUT: The 0MQ spec http://rfc.zeromq.org/spec:36 /// says "message content defined as one 0MQ frame. ZRE does not support multi-frame message contents." /// ...on the other hand, it appears that zeromq/zyre also supports multi-frame contents, as per the top of zyre.c /// This C# implementation allows multi-frame contents. /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="content">See warning above</param> /// <returns>true if message was successfully sent</returns> public static bool SendWhisper(IOutgoingSocket socket, ushort sequence, NetMQMessage content) { var msg = new ZreMsg { Id = MessageId.Whisper, Whisper = { Version = 2, Sequence = sequence, Content = content } }; return msg.Send(socket); }
/// <summary> /// Send message to one peer /// </summary> /// <param name="peer">The peer to get msg</param> /// <param name="msg">the message to send</param> public void SendMessageToPeer(ZrePeer peer, ZreMsg msg) { peer.Send(msg); }
/// <summary> /// Check if messages were lost from peer, returns true if they were /// </summary> /// <param name="msg"></param> /// <returns>true if we have lost one or more message</returns> internal bool MessagesLost(ZreMsg msg) { Debug.Assert(msg != null); // The sequence number set by the peer and our own calculated sequence number should be the same. if (msg.Id == ZreMsg.MessageId.Hello) { // HELLO always MUST have sequence = 1 _wantSequence = 1; } else { _wantSequence = _wantSequence == UshortMax ? (ushort) 0 : ++_wantSequence; } if (_wantSequence != msg.Sequence) { _loggerDelegate?.Invoke($"Sequence error for peer={Name} expected={_wantSequence}, got={msg.Sequence}"); return true; } return false; }
/// <summary> /// Send a Join message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <param name="group"></param> /// <param name="status"></param> /// <returns>true if message was successfully sent</returns> public static bool SendJoin(IOutgoingSocket socket, ushort sequence, string group, byte status) { var msg = new ZreMsg { Id = MessageId.Join, Join = { Version = 2, Sequence = sequence, Group = group, Status = status, } }; return msg.Send(socket); }
/// <summary> /// Send message to all peers /// </summary> /// <param name="msg">the message to send</param> public void SendPeers(ZreMsg msg) { foreach (var peer in _peers.Values) { SendMessageToPeer(peer, msg); } }
/// <summary> /// Send a Ping message to the socket /// </summary> /// <param name="socket"></param> /// <param name="sequence"></param> /// <returns>true if message was successfully sent</returns> public static bool SendPing(IOutgoingSocket socket, ushort sequence) { var msg = new ZreMsg { Id = MessageId.Ping, Ping = { Version = 2, Sequence = sequence, } }; return msg.Send(socket); }
/// <summary> /// Find or create peer via its UUID /// </summary> /// <param name="uuid">the identity of peer</param> /// <param name="endpoint">the endpoint to which we will connect the new peer</param> /// <returns>A peer (existing, or new one connected to endpoint)</returns> public ZrePeer RequirePeer(Guid uuid, string endpoint) { Debug.Assert(!string.IsNullOrEmpty(endpoint)); ZrePeer peer; if (_peers.TryGetValue(uuid, out peer)) { return peer; } // Purge any previous peer on same endpoint foreach (var existingPeer in _peers.Values) { PurgePeer(existingPeer, endpoint); } peer = ZrePeer.NewPeer(_peers, uuid); peer.SetOrigin(_name); peer.Connect(_uuid, _endpoint); // Handshake discovery by sending HELLO as first message var helloMessage = new ZreMsg { Id = ZreMsg.MessageId.Hello, Hello = { Endpoint = endpoint, Groups = _ownGroups.Keys.ToList(), Status = _status, Name = _name, Headers = _headers } }; peer.Send(helloMessage); return peer; }
/// <summary> /// Send a Hello message to the socket /// </summary> /// <param name="socket"></param> /// <param name="endpoint"></param> /// <param name="groups"></param> /// <param name="status"></param> /// <param name="name"></param> /// <param name="headers"></param> public static void SendHello(IOutgoingSocket socket, string endpoint, List<string> groups, byte status, string name, Dictionary<string, string> headers) { var msg = new ZreMsg { Id = MessageId.Hello, Hello = { Endpoint = endpoint, Groups = groups, Status = status, Name = name, Headers = headers } }; msg.Send(socket); }