/// <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];
            }
        }
Beispiel #10
0
        /// <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;
        }
Beispiel #11
0
        /// <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;
        }
Beispiel #12
0
        /// <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;
        }
Beispiel #13
0
 /// <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);
 }
Beispiel #15
0
        /// <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);
 }
Beispiel #17
0
        /// <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)
 {
 }
Beispiel #19
0
        /// <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);
                }
            }
        }
Beispiel #23
0
 /// <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);
 }