public void Send(ZreMessage msg) { foreach (var peer in _peers) { peer.Value.Send(msg.Duplicate()); } }
public void Send(ZreMessage msg) { if (_connected) { _sentSequence += 1; msg.Sequence = _sentSequence; if (_verbose) { Trace.WriteLine( string.Format( "({0}) send {1} to peer={2} sequence={3}", _origin, msg.Id, _name ?? "-", msg.Sequence)); } try { msg.Send(_mailbox); } catch (Exception) { if (_verbose) { Trace.WriteLine( string.Format( "({0}) disconnect from peer (EAGAIN): name={1}", _origin, _name ?? "-")); } } } }
public bool MessageLost(ZreMessage msg) { if (_verbose) { Trace.WriteLine( string.Format( "({0}) recv {1} from peer={2} sequence={3}", _origin, msg.Id, _name ?? "-", msg.Sequence)); } if (msg.Id == ZreMessageType.Hello) { _wantSequence = 1; } else { _wantSequence += 1; } if (_wantSequence != msg.Sequence) { Trace.WriteLine( string.Format( "({0}) seq error from peer={1} expect={2}, got={3}", _origin, _name ?? "-", _wantSequence, msg.Sequence)); return true; } return false; }
public ZreMessage Duplicate() { ZreMessage clone = new ZreMessage(_id); if (_routingId != null) { clone._routingId = _routingId.Duplicate(); } switch (_id) { case ZreMessageType.Hello: clone._version = _version; clone._sequence = _sequence; clone._endpoint = _endpoint; clone._groups = new HashSet<string>(_groups); clone._status = _status; clone._name = _name; clone._headers = new Dictionary<string, string>(_headers); break; case ZreMessageType.Whisper: clone._version = _version; clone._sequence = _sequence; clone._content = new NetMQMessage(_content.Select(x => x.Duplicate())); break; case ZreMessageType.Shout: clone._version = _version; clone._sequence = _sequence; clone._group = _group; clone._content = new NetMQMessage(_content.Select(x => x.Duplicate())); break; case ZreMessageType.Join: clone._version = _version; clone._sequence = _sequence; clone._group = _group; clone._status = _status; break; case ZreMessageType.Leave: clone._version = _version; clone._sequence = _sequence; clone._group = _group; clone._status = _status; break; case ZreMessageType.Ping: clone._version = _version; clone._sequence = _sequence; break; case ZreMessageType.PingOk: clone._version = _version; clone._sequence = _sequence; break; } return clone; }
public static bool IsZreMessage(NetMQMessage message) { if (message == null) { return false; } var frame = message.First; var msg = new ZreMessage(0); msg._needle = frame.ToByteArray(); msg._needleReader = new BinaryReader(new MemoryStream(msg._needle)); msg._needleWriter = new BinaryWriter(new MemoryStream(msg._needle)); msg._ceiling = msg._needle.Length + frame.MessageSize; var signature = msg._needleReader.GetNumber2(); if (signature != (0xAAA0 | ZreConstants.ProtocolSignature)) { return false; } msg._id = (ZreMessageType)msg._needleReader.GetNumber1(); switch (msg._id) { case ZreMessageType.Hello: case ZreMessageType.Whisper: case ZreMessageType.Shout: case ZreMessageType.Join: case ZreMessageType.Leave: case ZreMessageType.Ping: case ZreMessageType.PingOk: return true; } return false; }
public static NetMQMessage Encode(ZreMessage msg) { var message = new NetMQMessage(); var frameSize = 2 + 1; // Signature and message ID switch (msg._id) { case ZreMessageType.Hello: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; // endpoint is a string with 1-byte length frameSize++; // Size is one octet if (!string.IsNullOrEmpty(msg._endpoint)) { frameSize += msg._endpoint.Length; } // groups is an array of strings frameSize += 4; // Size is 4 octets if (msg._groups != null && msg._groups.Count > 0) { // Add up size of list contents foreach (var @group in msg._groups) { frameSize += 4 + @group.Length; } } // status is a 1-byte integer frameSize += 1; // name is a string with 1-byte length frameSize++; // Size is one octet if (!string.IsNullOrEmpty(msg._name)) { frameSize += msg._name.Length; } // headers is an array of key=value strings frameSize += 4; // Size is 4 octets if (msg._headers != null && msg._headers.Count > 0) { msg._headersBytes = 0; // Add up size of dictionary contents foreach (var header in msg._headers) { msg._headersBytes += 1 + header.Key.Length; msg._headersBytes += 4 + header.Value.Length; } } frameSize += msg._headersBytes; break; case ZreMessageType.Whisper: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; break; case ZreMessageType.Shout: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; // group is a string with 1-byte length frameSize++; // Size is one octet if (!string.IsNullOrEmpty(msg._group)) { frameSize += msg._group.Length; } break; case ZreMessageType.Join: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; // group is a string with 1-byte length frameSize++; // Size is one octet if (!string.IsNullOrEmpty(msg._group)) { frameSize += msg._group.Length; } // status is a 1-byte integer frameSize += 1; break; case ZreMessageType.Leave: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; // group is a string with 1-byte length frameSize++; // Size is one octet if (!string.IsNullOrEmpty(msg._group)) { frameSize += msg._group.Length; } // status is a 1-byte integer frameSize += 1; break; case ZreMessageType.Ping: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; break; case ZreMessageType.PingOk: // version is a 1-byte integer frameSize += 1; // sequence is a 2-byte integer frameSize += 2; break; } var frame = new NetMQFrame(frameSize); msg._needle = frame.ToByteArray(); msg._needleReader = new BinaryReader(new MemoryStream(msg._needle)); msg._needleWriter = new BinaryWriter(new MemoryStream(msg._needle)); msg._needleWriter.PutNumber2(0xAAA0 | ZreConstants.ProtocolSignature); msg._needleWriter.PutNumber1((byte)msg._id); switch (msg._id) { case ZreMessageType.Hello: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); if (!string.IsNullOrEmpty(msg._endpoint)) { msg._needleWriter.PutString(msg._endpoint); } else { msg._needleWriter.PutNumber1(0); // Empty string } if (msg._groups != null) { msg._needleWriter.PutNumber4(msg._groups.Count); foreach (var @group in msg._groups) { msg._needleWriter.PutLongString(@group); } } else { msg._needleWriter.PutNumber4(0); // Empty string array } msg._needleWriter.PutNumber1(msg._status); if (!string.IsNullOrEmpty(msg._name)) { msg._needleWriter.PutString(msg._name); } else { msg._needleWriter.PutNumber1(0); // Empty string } if (msg._headers != null) { msg._needleWriter.PutNumber4(msg._headers.Count); foreach (var header in msg._headers) { msg._needleWriter.PutString(header.Key); msg._needleWriter.PutLongString(header.Value); } } else { msg._needleWriter.PutNumber4(0); // Empty dictionary } break; case ZreMessageType.Whisper: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); break; case ZreMessageType.Shout: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); if (!string.IsNullOrEmpty(msg._group)) { msg._needleWriter.PutString(msg._group); } else { msg._needleWriter.PutNumber1(0); // Empty string } break; case ZreMessageType.Join: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); if (!string.IsNullOrEmpty(msg._group)) { msg._needleWriter.PutString(msg._group); } else { msg._needleWriter.PutNumber1(0); // Empty string } msg._needleWriter.PutNumber1(msg._status); break; case ZreMessageType.Leave: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); if (!string.IsNullOrEmpty(msg._group)) { msg._needleWriter.PutString(msg._group); } else { msg._needleWriter.PutNumber1(0); // Empty string } msg._needleWriter.PutNumber1(msg._status); break; case ZreMessageType.Ping: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); break; case ZreMessageType.PingOk: msg._needleWriter.PutNumber1(ZreConstants.ProtocolVersion); msg._needleWriter.PutNumber2(msg._sequence); break; } // Now send the data frame message.Append(frame); // Now send the message field if there is any if (msg._id == ZreMessageType.Whisper) { if (msg._content != null && !msg._content.IsEmpty) { while (!msg._content.IsEmpty) { var contentFrame = msg._content.Pop(); message.Append(contentFrame); } } else { message.AppendEmptyFrame(); } } // Now send the message field if there is any if (msg._id == ZreMessageType.Shout) { if (msg._content != null && !msg._content.IsEmpty) { while (!msg._content.IsEmpty) { var contentFrame = msg._content.Pop(); message.Append(contentFrame); } } else { message.AppendEmptyFrame(); } } return message; }
public static ZreMessage Decode(NetMQMessage message) { var msg = new ZreMessage(0); var frame = message.Pop(); if (frame == null) { return null; } msg._needle = frame.ToByteArray(); msg._needleReader = new BinaryReader(new MemoryStream(msg._needle)); msg._needleWriter = new BinaryWriter(new MemoryStream(msg._needle)); msg._ceiling = msg._needle.Length + frame.MessageSize; var signature = msg._needleReader.GetNumber2(); if (signature != (0xAAA0 | ZreConstants.ProtocolSignature)) { return null; } msg._id = (ZreMessageType)msg._needleReader.GetNumber1(); switch (msg._id) { case ZreMessageType.Hello: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); msg._endpoint = msg._needleReader.GetString(); var listSize = msg._needleReader.GetNumber4(); msg._groups = new HashSet<string>(); while (listSize-- > 0) { var groupName = msg._needleReader.GetLongString(); msg._groups.Add(groupName); } msg._status = msg._needleReader.GetNumber1(); msg._name = msg._needleReader.GetString(); var hashSize = msg._needleReader.GetNumber4(); msg._headers = new ConcurrentDictionary<string, string>(); while (hashSize-- > 0) { var key = msg._needleReader.GetString(); var value = msg._needleReader.GetLongString(); msg._headers.Add(key, value); } } break; case ZreMessageType.Whisper: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); // Get zero or more remaining frames, leaving current // frame untouched msg._content = new NetMQMessage(); while (!message.IsEmpty) { msg._content.Append(message.Pop()); } if (msg._content.IsEmpty) { msg._content.AppendEmptyFrame(); } } break; case ZreMessageType.Shout: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); msg._group = msg._needleReader.GetString(); // Get zero or more remaining frames, leaving current // frame untouched msg._content = new NetMQMessage(); while (!message.IsEmpty) { msg._content.Append(message.Pop()); } if (msg._content.IsEmpty) { msg._content.AppendEmptyFrame(); } } break; case ZreMessageType.Join: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); msg._group = msg._needleReader.GetString(); msg._status = msg._needleReader.GetNumber1(); } break; case ZreMessageType.Leave: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); msg._group = msg._needleReader.GetString(); msg._status = msg._needleReader.GetNumber1(); } break; case ZreMessageType.Ping: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); } break; case ZreMessageType.PingOk: { msg._version = msg._needleReader.GetNumber1(); if (msg._version != ZreConstants.ProtocolVersion) { return null; } msg._sequence = msg._needleReader.GetNumber2(); } break; } return msg; }
private ZrePeer RequirePeer(Guid identity, string endpoint) { ZrePeer peer; if (_peers.TryGetValue(identity, out peer)) { return peer; } // Purge any previous peer on same endpoint PurgePeer(endpoint); peer = new ZrePeer(_context, identity); _peers.TryAdd(identity, peer); peer.Origin = _name; peer.Verbose = _verbose; peer.Connect(_identity, endpoint); // Handshake discovery by sending HELLO as first message var groups = _ownGroups.Keys.ToArray(); var headers = new ConcurrentDictionary<string, string>(_headers); var msg = new ZreMessage(ZreMessageType.Hello); msg.Endpoint = _endpoint; msg.Groups.UnionWith(groups); msg.Status = _status; msg.Name = _name; msg.Headers = headers; peer.Send(msg); return peer; }
private void OnPipeReady(object sender, NetMQSocketEventArgs e) { // Get the whole message off the pipe in one go var message = e.Socket.ReceiveMultipartMessage(); if (message == null) { return; } var command = message.Pop().ConvertToString(); switch (command) { case Zre.StartCommand: if (Start()) { e.Socket.SignalOK(); } else { e.Socket.SignalError(); } break; case Zre.StopCommand: if (Stop()) { e.Socket.SignalOK(); } else { e.Socket.SignalError(); } break; case Zre.SetNameCommand: _name = message.Pop().ConvertToString(); break; case Zre.GetNameCommand: _pipe.SendFrame(_name); break; case Zre.SetUuidCommand: _identity = new Guid(message.Pop().ToByteArray()); break; case Zre.GetUuidCommand: _pipe.SendFrame(_identity.ToString()); break; case Zre.SetIntervalCommand: _interval = TimeSpan.FromMilliseconds(message.Pop().ConvertToInt32()); break; case Zre.SetVerboseCommand: _verbose = true; break; case Zre.SetHeaderCommand: { var key = message.Pop().ConvertToString(); var value = message.Pop().ConvertToString(); _headers[key] = value; } break; case Zre.SetPortCommand: _beaconPort = message.Pop().ConvertToInt32(); break; case Zre.WhisperCommand: { // Get peer to send message to var identityString = message.Pop().ConvertToString(); Guid identity; if (Guid.TryParse(identityString, out identity)) { // Send frame on out to peer's mailbox, drop message // if peer doesn't exist (may have been destroyed) ZrePeer peer; if (_peers.TryGetValue(identity, out peer)) { var msg = new ZreMessage(ZreMessageType.Whisper); msg.Content = message; peer.Send(msg); } } } break; case Zre.ShoutCommand: { // Get group to send message to var groupName = message.Pop().ConvertToString(); ZreGroup group; if (_peerGroups.TryGetValue(groupName, out group)) { var msg = new ZreMessage(ZreMessageType.Shout); msg.Group = groupName; msg.Content = message; group.Send(msg); } } break; case Zre.JoinCommand: { var groupName = message.Pop().ConvertToString(); ZreGroup @group; if (!_ownGroups.TryGetValue(groupName, out group)) { @group = new ZreGroup(groupName); _ownGroups[groupName] = @group; var joinMsg = new ZreMessage(ZreMessageType.Join); joinMsg.Group = groupName; joinMsg.Status = ++_status; foreach (var zrePeer in _peers) { zrePeer.Value.Send(joinMsg.Duplicate()); } if (_verbose) { Trace.WriteLine(string.Format("({0}) JOIN group={1}", _name, groupName)); } } } break; case Zre.LeaveCommand: { var groupName = message.Pop().ConvertToString(); ZreGroup @group; if (_ownGroups.TryGetValue(groupName, out group)) { var leaveMsg = new ZreMessage(ZreMessageType.Leave); leaveMsg.Group = groupName; leaveMsg.Status = ++_status; foreach (var zrePeer in _peers) { zrePeer.Value.Send(leaveMsg.Duplicate()); } _ownGroups.TryRemove(groupName, out @group); if (_verbose) { Trace.WriteLine(string.Format("({0}) LEAVE group={1}", _name, groupName)); } } } break; case Zre.GetPeersCommand: { var peerNames = _peers.Keys.ToArray(); using (var msgStream = new MemoryStream()) using (var writer = new BinaryWriter(msgStream)) { foreach (var peerName in peerNames) { writer.PutString(peerName.ToString()); } _pipe.SendFrame(msgStream.ToArray()); } } break; case Zre.GetPeerGroupsCommand: { } break; case Zre.GetOwnGroupsCommand: { } break; case Zre.GetPeerEndpointCommand: { var identityString = message.Pop().ConvertToString(); Guid identity; if (Guid.TryParse(identityString, out identity)) { ZrePeer peer; if (_peers.TryGetValue(identity, out peer)) { _pipe.SendFrame(peer.Endpoint); } } } break; case Zre.GetPeerHeaderCommand: { var identityString = message.Pop().ConvertToString(); var key = message.Pop().ConvertToString(); Guid identity; if (Guid.TryParse(identityString, out identity)) { ZrePeer peer; if (_peers.TryGetValue(identity, out peer)) { var header = peer.GetHeader(key, defaultValue: string.Empty); _pipe.SendFrame(header); } else { _pipe.SendFrame(string.Empty); } } } break; case Zre.SetEndPointCommand: { StartGossip(); var endpoint = message.Pop().ConvertToString(); try { _inbox.Bind(endpoint); _endpoint = endpoint; _pipe.SignalOK(); } catch (Exception) { _pipe.SignalError(); } } break; case Zre.GossipBindCommand: { StartGossip(); _gossipBind = message.Pop().ConvertToString(); _gossip.SendMoreFrame("BIND").SendFrame(_gossipBind); } break; case Zre.GossipConnectCommand: { StartGossip(); _gossipConnect = message.Pop().ConvertToString(); _gossip.SendMoreFrame("CONNECT").SendFrame(_gossipConnect); } break; case Zre.SetInterfaceCommand: { _beaconInterfaceName = message.Pop().ConvertToString(); } break; case NetMQActor.EndShimMessage: _poller.Cancel(); break; } }
private void OnPingPeer(object sender, NetMQTimerEventArgs e) { foreach (var peer in _peers.Values) { var clockTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; if (clockTime >= peer.ExpiredAt) { if (_verbose) { Trace.WriteLine( string.Format( "({0}) peer expired name={1} endpoint={2}", _name, peer.Name, peer.Endpoint)); } RemovePeer(peer); } else if (clockTime >= peer.EvasiveAt) { // If peer is being evasive, force a TCP ping. // TODO: do this only once for a peer in this state; // it would be nicer to use a proper state machine // for peer management. if (_verbose) { Trace.WriteLine( string.Format( "({0}) peer seems dead/slow name={1} endpoint={2}", _name, peer.Name, peer.Endpoint)); } var ping = new ZreMessage(ZreMessageType.Ping); peer.Send(ping); // Inform the calling application this peer is being evasive _pipe .SendMoreFrame("EVASIVE") .SendMoreFrame(peer.Identity.ToString()) .SendFrame(peer.Name); } } }
private void OnInboxReady(object sender, NetMQSocketEventArgs e) { var msg = ZreMessage.Receive(e.Socket); if (msg == null) { return; } if (msg.RoutingId.MessageSize != ZreConstants.ZreUuidLength) { return; } // Router socket tells us the identity of this peer // Identity must be [1] followed by 16-byte UUID, ignore the [1] var uuidBuffer = new byte[16]; var uuidReceived = msg.RoutingId.ToByteArray(); Array.Copy(uuidReceived, 1, uuidBuffer, 0, uuidBuffer.Length); var senderId = new Guid(uuidBuffer); // On HELLO we may create the peer if it's unknown // On other commands the peer must already exist ZrePeer peer; _peers.TryGetValue(senderId, out peer); if (msg.Id == ZreMessageType.Hello) { if (peer != null) { // Remove fake peers if (peer.Ready) { RemovePeer(peer); } else if (peer.Endpoint == _endpoint) { // We ignore HELLO, if peer has same endpoint as current node return; } } peer = RequirePeer(senderId, msg.Endpoint); peer.Ready = true; } if (peer == null || !peer.Ready) { return; } if (peer.MessageLost(msg)) { Trace.WriteLine(string.Format("({0}) messages lost from {1}", _name, peer.Name)); RemovePeer(peer); return; } // Now process each command if (msg.Id == ZreMessageType.Hello) { // Store properties from HELLO command into peer peer.Name = msg.Name; peer.Headers = msg.Headers; // Tell the caller about the peer _pipe .SendMoreFrame("ENTER") .SendMoreFrame(peer.Identity.ToString()) .SendMoreFrame(peer.Name) .SendMoreFrame(peer.Headers.PackHeaders()) .SendFrame(msg.Endpoint); if (_verbose) { Trace.WriteLine( string.Format( "({0}) ENTER name={1} endpoint={2}", _name, peer.Name, peer.Endpoint)); } // Join peer to listed groups foreach (var @group in msg.Groups) { JoinPeerGroup(peer, @group); } // Now take peer's status from HELLO, after joining groups peer.Status = msg.Status; } else if (msg.Id == ZreMessageType.Whisper) { // Pass up to caller API as WHISPER event _pipe .SendMoreFrame(Zre.WhisperCommand) .SendMoreFrame(senderId.ToString()) .SendMoreFrame(peer.Name) .SendMultipartMessage(msg.Content); } else if (msg.Id == ZreMessageType.Shout) { // Pass up to caller API as SHOUT event _pipe .SendMoreFrame(Zre.ShoutCommand) .SendMoreFrame(senderId.ToString()) .SendMoreFrame(peer.Name) .SendMoreFrame(msg.Group) .SendMultipartMessage(msg.Content); } else if (msg.Id == ZreMessageType.Ping) { var pong = new ZreMessage(ZreMessageType.PingOk); peer.Send(pong); } else if (msg.Id == ZreMessageType.Join) { JoinPeerGroup(peer, msg.Group); } else if (msg.Id == ZreMessageType.Leave) { LeavePeerGroup(peer, msg.Group); } // Activity from peer resets peer timers peer.Refresh(); }