/// <summary> /// Initializes a new instance of the <see cref="InternalMessage" /> class. /// </summary> /// <param name="destination">The destination of the message.</param> /// <param name="sender">The sender of the message.</param> /// <param name="type">The type of message.</param> /// <param name="data">The data to go along with the message.</param> /// <param name="waitingForResponse">Whether this message is waiting for a response.</param> /// <param name="messageId">The id of the message if it has one.</param> /// <param name="sendResult">The result object passed back after sending a message.</param> /// <param name="responseResult"> /// The result object passed back after sending a message waiting for a response. /// </param> /// <param name="needsApprovedConnection">A value indicating whether the connection needs to have been approved.</param> /// <returns>The composed message to be sent over the wire to the receiving node.</returns> private InternalMessage(NodeProperties destination, NodeProperties sender, MessageType type, string data, bool waitingForResponse, uint? messageId, MessageSendResult sendResult, MessageResponseResult responseResult, bool needsApprovedConnection) { _destination = destination; _sender = sender; _type = type; _data = data; _waitingForResponse = waitingForResponse; _messageId = messageId; _sendResult = sendResult; _responseResult = responseResult; _needsApprovedConnection = needsApprovedConnection; }
/// <summary> /// Creates a message that is a response to another message. /// </summary> /// <param name="destination">The destination of the message.</param> /// <param name="sender">The sender of the message.</param> /// <param name="type">The type of the message.</param> /// <param name="data">The data contained in the message.</param> /// <param name="messageId">The id of the message.</param> /// <param name="result">The object to put the results in.</param> /// <returns>A message that is a response to another message.</returns> public static InternalMessage CreateResponseMessage( NodeProperties destination, NodeProperties sender, MessageType type, string data, uint messageId, MessageSendResult result) { return new InternalMessage(destination, sender, type, data, false, messageId, result, null, false); }
/// <summary> /// Sends a message to another node and awaits a response. /// </summary> /// <param name="sendTo">The node to send the message to.</param> /// <param name="message">The message to send.</param> /// <returns> /// A response object that contains whether the message was successfully sent and the /// response from the receiver. /// </returns> public MessageResponseResult SendMessageResponse(NodeProperties sendTo, string message) { return SendMessageResponseInternal(sendTo, MessageType.User, message, false); }
/// <inheritdoc></inheritdoc> protected override void ApprovalRequestGranted(NodeProperties node) { ConnectToMembers(node); }
/// <inheritdoc></inheritdoc> protected override void ApprovalRequestGranted(NodeProperties node) { if (_startup) { lock (_successorPredecessorLockObject) { var response = SendMessageResponseInternal(node, MessageType.System, "findsuccessor|" + _id, false); if (response.SendResult == SendResults.Success && response.ResponseResult == ResponseResults.Success) { if (response.ResponseMessage.Data == string.Empty) { var idResponse = SendMessageResponseInternal(node, MessageType.System, "id", false); if (idResponse.SendResult == SendResults.Success && idResponse.ResponseResult == ResponseResults.Success) { _successor = node; _successorId = int.Parse(idResponse.ResponseMessage.Data); } } else { string[] result = response.ResponseMessage.Data.Split('|'); _successor = new NodeProperties(result[0]); _successorId = int.Parse(result[1]); } } } _startup = false; } }
/// <summary> /// A node has just stabilized to be connected to this node. /// </summary> /// <param name="node">The node that just connected to this node.</param> protected void Notify(NodeProperties node) { lock (_successorPredecessorLockObject) { GetApproval(node); var result = SendMessageResponseInternal(node, MessageType.System, "id", true); if (result.SendResult == SendResults.Success && result.ResponseResult == ResponseResults.Success) { int id = int.Parse(result.ResponseMessage.Data); if (_predecessor == null || IsBetween(id, _predecessorId, _id)) { _predecessor = node; _predecessorId = id; } } } }
protected virtual void NodeDisconnected(NodeProperties node) { _sendingConnections.Remove(node); }
/// <summary> /// Initializes a new instance of the <see cref="InternalMessage" /> class. /// </summary> /// <param name="rawMessage">The message that will be parsed.</param> /// <param name="sender">The sender of the message.</param> public InternalMessage(string rawMessage, NodeProperties sender) { _rawMessage = rawMessage; int index = 0; while (index < rawMessage.Length) { if (!char.IsDigit(rawMessage[index])) { switch (rawMessage[index]) { case 'f': _waitingForResponse = false; _messageId = 0; break; case 't': _waitingForResponse = true; break; } break; } ++index; } ++index; uint messageId = 0; while (index < rawMessage.Length) { if (!char.IsDigit(rawMessage[index])) { break; } messageId *= 10; messageId += (uint)char.GetNumericValue(rawMessage[index]); ++index; } if (messageId == uint.MaxValue) { _messageId = null; } else { _messageId = messageId; } while (index < rawMessage.Length) { if (!char.IsDigit(rawMessage[index])) { switch (rawMessage[index]) { case 'a': _type = MessageType.Approval; break; case 'n': _type = MessageType.Neighbors; break; case 'p': _type = MessageType.Ping; break; case 's': _type = MessageType.System; break; case 'u': _type = MessageType.User; break; default: _type = MessageType.Unknown; break; } break; } ++index; } ++index; int senderPort = 0; while (index < rawMessage.Length) { if (!char.IsDigit(rawMessage[index])) { _sender = new NodeProperties(sender.IpAddress, senderPort); _data = rawMessage.Substring(index + 1, rawMessage.Length - (index + 1)); break; } senderPort *= 10; senderPort += (int)char.GetNumericValue(rawMessage[index]); ++index; } }
/// <summary> /// Gets a network connection to the specified node, creating the connection if need be. /// </summary> /// <param name="connectTo">The node to connect to.</param> /// <returns> /// The network connection associated with that node, or null if it could not be created. /// </returns> protected async Task<NetworkConnection> GetUnapprovedNetworkConnection(NodeProperties connectTo) { bool reconnect; lock (_sendingLockObject) { reconnect = !_sendingConnections.ContainsKey(connectTo); if (reconnect) { _sendingConnections[connectTo] = null; } } if (reconnect) { TcpClient client = null; try { _logger.Write("Attempting connection to " + connectTo, LogLevels.Info); client = new TcpClient(); client.NoDelay = true; await client.ConnectAsync(connectTo.IpAddress, connectTo.Port).ConfigureAwait(false); NetworkConnection connection = new NetworkConnection { Client = client, LastPingReceived = DateTime.UtcNow }; lock (_sendingLockObject) { _sendingConnections[connectTo] = connection; } _logger.Write("Connection to " + connectTo + " successful", LogLevels.Info); return connection; } catch { _logger.Write("Connection to " + connectTo + " failed", LogLevels.Warning); if (client != null) { client.Close(); } lock (_sendingLockObject) { NodeDisconnected(connectTo); } return null; } } bool waiting; lock (_sendingLockObject) { waiting = _sendingConnections[connectTo] == null; } while (waiting) { await Task.Delay(1).ConfigureAwait(false); lock (_sendingLockObject) { waiting = _sendingConnections.ContainsKey(connectTo) && _sendingConnections[connectTo] == null; } } lock (_sendingLockObject) { return !_sendingConnections.ContainsKey(connectTo) ? null : _sendingConnections[connectTo]; } }
/// <summary> /// Determines whether a node represents the current node. /// </summary> /// <param name="node">The node to check.</param> /// <returns>True if the node represents the current node.</returns> protected bool IsSelf(NodeProperties node) { if (_currentIpAddresses == null) { _currentIpAddresses = Dns.GetHostAddresses(Dns.GetHostName()); } return _currentIpAddresses.Contains(node.IpAddress) && node.Port == _port; }
/// <summary> /// Gets a connection to the specified node as long as that node has been approved to be /// connected to the network. /// </summary> /// <param name="connectTo">The node to connect to.</param> /// <returns> /// The network connection associated with that node, or null if it could not be found. /// </returns> protected NetworkConnection GetApprovedNetworkConnection(NodeProperties connectTo) { lock (_sendingLockObject) { if (_sendingConnections.ContainsKey(connectTo) && _sendingConnections[connectTo] != null && _sendingConnections[connectTo].Approved) { return _sendingConnections[connectTo]; } } return null; }
/// <summary> /// Gets the approval of the node to join the network. /// </summary> /// <param name="node">The node to get the approval of.</param> /// <returns>A value indicating whether approval was granted.</returns> protected bool GetApproval(NodeProperties node) { lock (_sendingLockObject) { if (_sendingConnections.ContainsKey(node) && _sendingConnections[node] != null && _sendingConnections[node].Approved) { return true; } } var result = SendMessageResponseInternal(node, MessageType.Approval, GetNetworkType(), false); if (result.SendResult == SendResults.Success && result.ResponseResult == ResponseResults.Success) { if (result.ResponseMessage.Data == "approved") { lock (_sendingLockObject) { var connection = _sendingConnections[node]; if (connection != null) { connection.Approved = true; } } lock (_recentlyApprovedNodesLockObject) { _recentlyApprovedNodes.Enqueue(new ApprovedNodeDetails(node, false)); } return true; } } return false; }
/// <summary> /// A function to be called on the approved node once a node has been approved access. /// </summary> /// <param name="node">The node the approval request was granted to.</param> protected abstract void ApprovalRequestGranted(NodeProperties node);
/// <summary> /// Creates a message to be sent. /// </summary> /// <param name="destination">The destination of the message.</param> /// <param name="sender">The sender of the message.</param> /// <param name="type">The type of the message.</param> /// <param name="data">The data contained in the message.</param> /// <param name="result">The object to put the results in.</param> /// <param name="needsApprovedConnection">A value indicating whether the connection needs to have been approved.</param> /// <returns>A message to be sent.</returns> public static InternalMessage CreateSendMessage( NodeProperties destination, NodeProperties sender, MessageType type, string data, MessageSendResult result, bool needsApprovedConnection) { return new InternalMessage(destination, sender, type, data, false, null, result, null, needsApprovedConnection); }
/// <summary> /// Sends a message to a node. /// </summary> /// <param name="sendTo">The node to send the message to.</param> /// <param name="type">The type of message to send.</param> /// <param name="message">The message to send.</param> /// <param name="needsApprovedConnection"> /// A value indicating whether the connection needs to have been approved. /// </param> /// <returns>A value indicating whether the message was successfully sent.</returns> protected MessageSendResult SendMessageInternal(NodeProperties sendTo, MessageType type, string message, bool needsApprovedConnection) { var result = new MessageSendResult(); var composedMessage = InternalMessage.CreateSendMessage( sendTo, new NodeProperties("localhost", _port), type, message, result, needsApprovedConnection); lock (_messagesToSendLockObject) { _messagesToSend.Enqueue(composedMessage); } return result; }
/// <summary> /// Creates a message to be sent and that is expecting a response. /// </summary> /// <param name="destination">The destination of the message.</param> /// <param name="sender">The sender of the message.</param> /// <param name="type">The type of the message.</param> /// <param name="data">The data contained in the message.</param> /// <param name="messageId">The id of the message.</param> /// <param name="result">The object to put the results in.</param> /// <param name="needsApprovedConnection">A value indicating whether the connection needs to have been approved.</param> /// <returns>A message to be sent and that is expecting a response.</returns> public static InternalMessage CreateSendResponseMessage( NodeProperties destination, NodeProperties sender, MessageType type, string data, uint messageId, MessageResponseResult result, bool needsApprovedConnection) { return new InternalMessage(destination, sender, type, data, true, messageId, null, result, needsApprovedConnection); }
/// <summary> /// Sends a message to a node and awaits a response. /// </summary> /// <param name="sendTo">The node to send the message to.</param> /// <param name="type">The type of message to send.</param> /// <param name="message">The message to send.</param> /// <param name="needsApprovedConnection"> /// A value indicating whether the connection needs to have been approved. /// </param> /// <returns>A value indicating whether the message was successfully sent.</returns> protected MessageResponseResult SendMessageResponseInternal(NodeProperties sendTo, MessageType type, string message, bool needsApprovedConnection) { uint id = (uint)Interlocked.Increment(ref _messageIdCounter); lock (_responsesLockObject) { _responses[id] = null; } var result = new MessageResponseResult(); var composedMessage = InternalMessage.CreateSendResponseMessage( sendTo, new NodeProperties("localhost", _port), type, message, id, result, needsApprovedConnection); lock (_messagesToSendLockObject) { _messagesToSend.Enqueue(composedMessage); } return result; }
/// <inheritdoc></inheritdoc> protected override void ApprovalGranted(NodeProperties node) { }
/// <summary> /// The run function for the connection listener thread. /// </summary> private void ConnectionListenerThreadRun() { while (_childThreadsRunning) { try { var incomingTcpClient = _connectionListener.AcceptTcpClient(); var ipEndPoint = (IPEndPoint)incomingTcpClient.Client.RemoteEndPoint; var incomingNodeProperties = new NodeProperties(ipEndPoint.Address.MapToIPv4(), ipEndPoint.Port); _logger.Write("Connection received from " + incomingNodeProperties, LogLevels.Info); lock (_receivingLockObject) { _receivingConnections[incomingNodeProperties] = new NetworkConnection { Client = incomingTcpClient, LastPingReceived = DateTime.UtcNow }; } } catch (Exception) { // Assuming the error was because we are stopping accepting connections or it // was a connection error in which case it can be retried. } } }
protected override void NodeDisconnected(NodeProperties node) { base.NodeDisconnected(node); lock (_successorPredecessorLockObject) { if (_successor.Equals(node)) { _successor = null; _successorId = -1; } if (_predecessor.Equals(node)) { _predecessor = null; _predecessorId = -1; } } lock (_fingerTableLockObject) { for (int i = 0; i < _fingerTable.Count; ++i) { if (_fingerTable[i].Item1.Equals(node)) { _fingerTable[i] = null; } } } }
/// <summary> /// Connects to all members of the specified node. /// </summary> /// <param name="node">The node to connect to the members of.</param> private void ConnectToMembers(NodeProperties node) { var result = GetRemoteNeighbors(node); if (result.SendResult == SendResults.Success && result.ResponseResult == ResponseResults.Success) { Logger.Write( "Attempting connection to the following neighbors: " + result.ResponseMessage.Data, LogLevels.Debug); foreach (var neighbor in result.ResponseMessage.Data.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) .ToList() .Select(e => new NodeProperties(e))) { GetApproval(neighbor); } } }
/// <summary> /// Tries to stabilize the network. /// </summary> protected void Stabilize() { lock (_successorPredecessorLockObject) { if (_successor == null) { _successor = _predecessor; _successorId = _predecessorId; // Only notify if we actually have someone to notify besides ourselves. if (_successor != null) { GetApproval(_successor); SendMessageInternal(_successor, MessageType.System, "notify", true); } } else { GetApproval(_successor); var result = SendMessageResponseInternal(_successor, MessageType.System, "predecessor", true); if (result.SendResult == SendResults.Success && result.ResponseResult == ResponseResults.Success) { string nodeString = result.ResponseMessage.Data; if (nodeString != string.Empty) { var node = new NodeProperties(nodeString); GetApproval(node); var idResult = SendMessageResponseInternal(node, MessageType.System, "id", true); if (idResult.SendResult == SendResults.Success && idResult.ResponseResult == ResponseResults.Success) { int id = int.Parse(idResult.ResponseMessage.Data); if (IsBetween(id, _id, _successorId)) { _successor = node; _successorId = id; } } } } SendMessageInternal(_successor, MessageType.System, "notify", true); } } }
/// <summary> /// Gets a list of the remote node's neighboring nodes. /// </summary> /// <param name="remoteNode">The remote node to retrieve the information from.</param> /// <returns> /// The nodes that the remote node is connected to, null if the call failed to reach the /// remote host. /// </returns> public MessageResponseResult GetRemoteNeighbors(NodeProperties remoteNode) { return SendMessageResponseInternal(remoteNode, MessageType.Neighbors, string.Empty, false); }