public DhtClient(int udpDhtPort, IDhtClientManager manager) { _manager = manager; IPEndPoint localEP; switch (Environment.OSVersion.Platform) { case PlatformID.Win32NT: if (Environment.OSVersion.Version.Major < 6) { //below vista _udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); localEP = new IPEndPoint(IPAddress.Any, udpDhtPort); } else { //vista & above _udpListener = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); _udpListener.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); _udpClient = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); _udpClient.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); localEP = new IPEndPoint(IPAddress.IPv6Any, udpDhtPort); } break; case PlatformID.Unix: //mono framework if (Socket.OSSupportsIPv6) { _udpListener = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); _udpClient = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); localEP = new IPEndPoint(IPAddress.IPv6Any, udpDhtPort); } else { _udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); localEP = new IPEndPoint(IPAddress.Any, udpDhtPort); } break; default: //unknown _udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); localEP = new IPEndPoint(IPAddress.Any, udpDhtPort); break; } try { _udpListener.Bind(localEP); } catch { localEP.Port = 0; _udpListener.Bind(localEP); } _localPort = (_udpListener.LocalEndPoint as IPEndPoint).Port; //init routing table _currentNode = new CurrentNode(udpDhtPort); _routingTable = new KBucket(_currentNode); //bind udp client socket to random port if (_udpClient.AddressFamily == AddressFamily.InterNetwork) { _udpClient.Bind(new IPEndPoint(IPAddress.Any, 0)); } else { _udpClient.Bind(new IPEndPoint(IPAddress.IPv6Any, 0)); } //start reading udp response packets _udpClientThread = new Thread(ReadResponsePacketsAsync); _udpClientThread.IsBackground = true; _udpClientThread.Start(_udpClient); //start reading udp query packets _udpListenerThread = new Thread(ReadQueryPacketsAsync); _udpListenerThread.IsBackground = true; _udpListenerThread.Start(_udpListener); //start health timer _healthTimer = new Timer(HealthTimerCallback, null, QUERY_TIMEOUT, Timeout.Infinite); }
private DhtRpcPacket Query(DhtRpcPacket packet, NodeContact contact) { Transaction transaction = new Transaction(contact.NodeID, contact.NodeEP); try { lock (_transactions) { _transactions.Add(packet.TransactionID, transaction); } lock (transaction) { lock (_sendBufferStream) { _sendBufferStream.Position = 0; packet.WriteTo(_sendBufferStream); if (_proxyEnabled) { _manager.SendDhtPacket(contact.NodeEP, _sendBufferStream.Buffer, 0, (int)_sendBufferStream.Position); } else { _udpClient.SendTo(_sendBufferStream.Buffer, 0, (int)_sendBufferStream.Position, SocketFlags.None, contact.NodeEP); } } if (!Monitor.Wait(transaction, QUERY_TIMEOUT)) { contact.IncrementRpcFailCount(); return(null); } //auto add contact or update last seen time if (contact.NodeID == null) { contact = transaction.ResponsePacket.SourceNode; } KBucket closestBucket = _routingTable.FindClosestBucket(contact.NodeID); NodeContact bucketContact = closestBucket.FindContactInCurrentBucket(contact.NodeID); if (bucketContact == null) { closestBucket.AddContactInCurrentBucket(contact); } else { bucketContact.UpdateLastSeenTime(); } return(transaction.ResponsePacket); } } finally { lock (_transactions) { _transactions.Remove(packet.TransactionID); } } }
private object QueryFind(NodeContact[] initialContacts, BinaryID nodeID, RpcQueryType queryType) { List <NodeContact> availableContacts = new List <NodeContact>(initialContacts); List <NodeContact> respondedContacts = new List <NodeContact>(); List <NodeContact> failedContacts = new List <NodeContact>(); NodeContact[] alphaContacts; int alpha = KADEMLIA_ALPHA; bool finalRound = false; while (true) { //pick alpha contacts to query from available contacts lock (availableContacts) { alphaContacts = PickClosestContacts(availableContacts, alpha); } if (alphaContacts.Length < 1) { //no contacts available to query further lock (respondedContacts) { if (respondedContacts.Count > KADEMLIA_K) { return(KBucket.GetClosestContacts(respondedContacts, nodeID, KADEMLIA_K)); } else if (respondedContacts.Count > 0) { return(respondedContacts.ToArray()); } } return(null); } object lockObj = new object(); List <NodeContact> receivedContacts = new List <NodeContact>(); List <PeerEndPoint> receivedPeers = null; if (queryType == RpcQueryType.FIND_PEERS) { receivedPeers = new List <PeerEndPoint>(); } lock (lockObj) { //query each alpha contact async foreach (NodeContact alphaContact in alphaContacts) { ThreadPool.QueueUserWorkItem(QueryFindAsync, new object[] { lockObj, queryType, alphaContact, nodeID, availableContacts, respondedContacts, failedContacts, receivedContacts, receivedPeers }); } //wait for any of the contact to return new contacts if (Monitor.Wait(lockObj, QUERY_TIMEOUT)) { //got reply! if (queryType == RpcQueryType.FIND_PEERS) { lock (receivedPeers) { if (receivedPeers.Count > 0) { return(receivedPeers.ToArray()); } } } lock (receivedContacts) { if (receivedContacts.Count < 1) { //current round failed to return any new closer nodes if (finalRound) { if (queryType == RpcQueryType.FIND_PEERS) { return(null); } lock (respondedContacts) { if (respondedContacts.Count > KADEMLIA_K) { return(KBucket.GetClosestContacts(respondedContacts, nodeID, KADEMLIA_K)); } else { return(respondedContacts.ToArray()); } } } else { //resend query to k closest node not already queried finalRound = true; alpha = KADEMLIA_K; } } else { finalRound = false; alpha = KADEMLIA_ALPHA; } } } } } }
public NodeContact[] GetKClosestContacts(BinaryNumber nodeID) { KBucket currentBucket = this; while (true) { NodeContact[] contacts = currentBucket._contacts; KBucket leftBucket = currentBucket._leftBucket; KBucket rightBucket = currentBucket._rightBucket; if ((contacts != null) || (leftBucket == null) || (rightBucket == null)) { #region find closest contacts from this bucket KBucket closestBucket = currentBucket; NodeContact[] closestContacts = null; if (closestBucket._contactCount >= DhtNode.KADEMLIA_K) { closestContacts = closestBucket.GetAllContacts(false); if (closestContacts.Length > DhtNode.KADEMLIA_K) { return(GetClosestContacts(closestContacts, nodeID, DhtNode.KADEMLIA_K)); } if (closestContacts.Length == DhtNode.KADEMLIA_K) { return(closestContacts); } if (closestBucket._parentBucket == null) { return(closestContacts); } } while (closestBucket._parentBucket != null) { KBucket parentBucket = closestBucket._parentBucket; closestContacts = parentBucket.GetAllContacts(false); if (closestContacts.Length > DhtNode.KADEMLIA_K) { return(GetClosestContacts(closestContacts, nodeID, DhtNode.KADEMLIA_K)); } if (closestContacts.Length == DhtNode.KADEMLIA_K) { return(closestContacts); } closestBucket = parentBucket; } if (closestContacts == null) { closestContacts = closestBucket.GetAllContacts(false); } return(closestContacts); #endregion } if ((leftBucket._bucketID & nodeID) == leftBucket._bucketID) { currentBucket = leftBucket; } else { currentBucket = rightBucket; } } }
private void ReadQueryPacketsAsync(object parameter) { Socket udpListener = parameter as Socket; EndPoint remoteEP; FixMemoryStream recvBufferStream = new FixMemoryStream(BUFFER_MAX_SIZE); FixMemoryStream sendBufferStream = new FixMemoryStream(BUFFER_MAX_SIZE); int bytesRecv; if (udpListener.AddressFamily == AddressFamily.InterNetwork) { remoteEP = new IPEndPoint(IPAddress.Any, 0); } else { remoteEP = new IPEndPoint(IPAddress.IPv6Any, 0); } try { while (true) { bytesRecv = udpListener.ReceiveFrom(recvBufferStream.Buffer, ref remoteEP); if (bytesRecv > 0) { recvBufferStream.Position = 0; recvBufferStream.SetLength(bytesRecv); IPEndPoint remoteNodeEP = remoteEP as IPEndPoint; if (NetUtilities.IsIPv4MappedIPv6Address(remoteNodeEP.Address)) { remoteNodeEP = new IPEndPoint(NetUtilities.ConvertFromIPv4MappedIPv6Address(remoteNodeEP.Address), remoteNodeEP.Port); } try { DhtRpcPacket request = new DhtRpcPacket(recvBufferStream, remoteNodeEP.Address); DhtRpcPacket response = ProcessPacket(request); //send response if (response != null) { sendBufferStream.Position = 0; response.WriteTo(sendBufferStream); udpListener.SendTo(sendBufferStream.Buffer, 0, (int)sendBufferStream.Position, SocketFlags.None, remoteEP); } //if contact doesnt exists then add contact else update last seen time KBucket closestBucket = _routingTable.FindClosestBucket(request.SourceNode.NodeID); NodeContact contact = closestBucket.FindContactInCurrentBucket(request.SourceNode.NodeID); if (contact == null) { //check if the closest bucket can accomodate another contact if (!closestBucket.IsCurrentBucketFull(true)) { ThreadPool.QueueUserWorkItem(AddContactAfterPingAsync, request.SourceNode); } } else { contact.UpdateLastSeenTime(); } } catch { } } } } catch (Exception ex) { Debug.Write("DhtClient.ReadQueryPacketsAsync", ex); } }
public bool RemoveContact(NodeContact contact) { KBucket currentBucket = this; while (true) { NodeContact[] contacts = currentBucket._contacts; KBucket leftBucket = currentBucket._leftBucket; KBucket rightBucket = currentBucket._rightBucket; lock (currentBucket) { if ((contacts != null) || (leftBucket == null) || (rightBucket == null)) { #region remove contact from this bucket if (currentBucket._contactCount <= DhtNode.KADEMLIA_K) { return(false); //k-bucket is not full and replacement cache is empty } bool contactRemoved = false; for (int i = 0; i < contacts.Length; i++) { if (contact.Equals(contacts[i])) { if (contacts[i].IsStale()) { //remove stale contact contacts[i] = null; currentBucket._contactCount--; currentBucket._lastChanged = DateTime.UtcNow; contactRemoved = true; } break; } } if (!contactRemoved) { return(false); //contact was not found or was not stale } //check parent bucket contact count and join the parent bucket if (currentBucket._parentBucket != null) { JoinBucket(currentBucket._parentBucket); } return(true); #endregion } } if ((leftBucket._bucketID & contact.NodeID) == leftBucket._bucketID) { currentBucket = leftBucket; } else { currentBucket = rightBucket; } } }
public bool AddContact(NodeContact contact) { KBucket currentBucket = this; while (true) { NodeContact[] contacts = currentBucket._contacts; KBucket leftBucket = currentBucket._leftBucket; KBucket rightBucket = currentBucket._rightBucket; lock (currentBucket) { if ((contacts != null) || (leftBucket == null) || (rightBucket == null)) { #region add contact in this bucket //search if contact already exists for (int i = 0; i < contacts.Length; i++) { if (contact.Equals(contacts[i])) { return(false); //contact already exists } } //try add contact for (int i = 0; i < contacts.Length; i++) { if (contacts[i] == null) { contacts[i] = contact; currentBucket._contactCount++; currentBucket._lastChanged = DateTime.UtcNow; return(true); } } //k-bucket is full so contact was not added //if current contact is not stale then find and replace with any existing stale contact if (!contact.IsStale()) { for (int i = 0; i < contacts.Length; i++) { if (contacts[i].IsStale()) { contacts[i] = contact; currentBucket._lastChanged = DateTime.UtcNow; return(true); } } } //no stale contact in this k-bucket to replace! if (contacts[0].IsCurrentNode) { //split current bucket and add contact! SplitBucket(currentBucket, contact); return(true); } else { //k-bucket is full! return(false); } #endregion } } if ((leftBucket._bucketID & contact.NodeID) == leftBucket._bucketID) { currentBucket = leftBucket; } else { currentBucket = rightBucket; } } }
private static void JoinBucket(KBucket parentBucket) { lock (parentBucket) { KBucket leftBucket = parentBucket._leftBucket; KBucket rightBucket = parentBucket._rightBucket; if ((leftBucket == null) || (rightBucket == null)) { return; } lock (leftBucket) { lock (rightBucket) { if ((leftBucket._contactCount + rightBucket._contactCount) > DhtNode.KADEMLIA_K * 2) { return; //child k-buckets have more contacts then parent can accomodate } NodeContact[] contacts = new NodeContact[DhtNode.KADEMLIA_K * 2]; int contactCount = 0; if ((leftBucket._contacts[0] != null) && leftBucket._contacts[0].IsCurrentNode) { foreach (NodeContact contact in leftBucket._contacts) { if (contact != null) { contacts[contactCount++] = contact; } } foreach (NodeContact contact in rightBucket._contacts) { if (contact != null) { contacts[contactCount++] = contact; } } } else { foreach (NodeContact contact in rightBucket._contacts) { if (contact != null) { contacts[contactCount++] = contact; } } foreach (NodeContact contact in leftBucket._contacts) { if (contact != null) { contacts[contactCount++] = contact; } } } parentBucket._contacts = contacts; parentBucket._contactCount = contactCount; parentBucket._leftBucket = null; parentBucket._rightBucket = null; } } } }
private object QueryFind(NodeContact[] initialContacts, BinaryNumber nodeID, DhtRpcType queryType) { if (initialContacts.Length < 1) { return(null); } List <NodeContact> seenContacts = new List <NodeContact>(initialContacts); List <NodeContact> learnedNotQueriedContacts = new List <NodeContact>(initialContacts); List <NodeContact> respondedContacts = new List <NodeContact>(); List <PeerEndPoint> receivedPeers = null; int alpha = KADEMLIA_ALPHA; bool finalRound = false; bool checkTerminationCondition = false; if (queryType == DhtRpcType.FIND_PEERS) { receivedPeers = new List <PeerEndPoint>(); } NodeContact previousClosestSeenContact = KBucket.GetClosestContacts(seenContacts, nodeID, 1)[0]; while (true) { NodeContact[] alphaContacts; //pick alpha contacts to query from learned contacts lock (learnedNotQueriedContacts) { alphaContacts = KBucket.GetClosestContacts(learnedNotQueriedContacts, nodeID, alpha); //remove selected alpha contacts from learned not queries contacts list foreach (NodeContact alphaContact in alphaContacts) { learnedNotQueriedContacts.Remove(alphaContact); } } if (alphaContacts.Length < 1) { checkTerminationCondition = true; } else { object lockObj = new object(); lock (lockObj) { //query each alpha contact async foreach (NodeContact alphaContact in alphaContacts) { Thread t = new Thread(delegate(object state) { DhtRpcPacket response; if (queryType == DhtRpcType.FIND_NODE) { response = Query(DhtRpcPacket.CreateFindNodePacketQuery(_currentNode, nodeID), alphaContact); } else { response = Query(DhtRpcPacket.CreateFindPeersPacketQuery(_currentNode, nodeID), alphaContact); } if ((response == null) || (response.Type != queryType)) { //time out or error //ignore contact by removing from seen contacts list lock (seenContacts) { seenContacts.Remove(alphaContact); } return; } //got reply! if ((queryType == DhtRpcType.FIND_PEERS) && (response.Peers.Length > 0)) { lock (receivedPeers) { foreach (PeerEndPoint peer in response.Peers) { if (!receivedPeers.Contains(peer)) { receivedPeers.Add(peer); } } } } //add alpha contact to responded contacts list lock (respondedContacts) { if (!respondedContacts.Contains(alphaContact)) { respondedContacts.Add(alphaContact); } } //add received contacts to learned contacts list lock (seenContacts) { lock (learnedNotQueriedContacts) { foreach (NodeContact contact in response.Contacts) { if (!seenContacts.Contains(contact)) { seenContacts.Add(contact); learnedNotQueriedContacts.Add(contact); } } } } //no pulse for final round to wait for all k contacts to respond. this allows any failed node contact to be removed from seen contacts list during the wait. lock (lockObj) { Monitor.Pulse(lockObj); } }); t.IsBackground = true; t.Start(); } //wait for any of the node contact to return new contacts if (Monitor.Wait(lockObj, QUERY_TIMEOUT)) { //got reply or final round! NodeContact currentClosestSeenContact; lock (seenContacts) { currentClosestSeenContact = KBucket.GetClosestContacts(seenContacts, nodeID, 1)[0]; } BinaryNumber previousDistance = nodeID ^ previousClosestSeenContact.NodeID; BinaryNumber currentDistance = nodeID ^ currentClosestSeenContact.NodeID; if (previousDistance <= currentDistance) { //current round failed to return a node contact any closer than the closest already seen if (finalRound) { //final round over, check for termination condition checkTerminationCondition = true; } else { //resend query to k closest node not already queried finalRound = true; alpha = KADEMLIA_K; } } else { //current closest seen contact is closer than previous closest seen contact previousClosestSeenContact = currentClosestSeenContact; finalRound = false; alpha = KADEMLIA_ALPHA; } } } } if (checkTerminationCondition) { checkTerminationCondition = false; //reset if (queryType == DhtRpcType.FIND_PEERS) { //check only in final round to get most peers lock (receivedPeers) { if (receivedPeers.Count > 0) { return(receivedPeers.ToArray()); } return(null); } } //lookup terminates when k closest seen contacts have responded NodeContact[] kClosestSeenContacts; lock (seenContacts) { kClosestSeenContacts = KBucket.GetClosestContacts(seenContacts, nodeID, KADEMLIA_K); } lock (respondedContacts) { bool success = true; foreach (NodeContact contact in kClosestSeenContacts) { if (!respondedContacts.Contains(contact)) { success = false; break; } } if (success) { return(kClosestSeenContacts); } if (alphaContacts.Length < 1) { return(KBucket.GetClosestContacts(respondedContacts, nodeID, KADEMLIA_K)); } } } } }
public bool RemoveContact(NodeContact contact) { KBucket currentBucket = this; while (true) { NodeContact[] contacts; KBucket leftBucket; KBucket rightBucket; lock (currentBucket) { contacts = currentBucket._contacts; leftBucket = currentBucket._leftBucket; rightBucket = currentBucket._rightBucket; if (contacts != null) { #region remove contact from this bucket if (currentBucket._contactCount <= DhtNode.KADEMLIA_K) { return(false); //k-bucket is not full and replacement cache is empty } for (int i = 0; i < contacts.Length; i++) { if (contact.Equals(contacts[i])) { if (contacts[i].IsStale()) { //remove stale contact contacts[i] = null; currentBucket._lastChanged = DateTime.UtcNow; KBucket bucket = currentBucket; do { Interlocked.Decrement(ref bucket._contactCount); bucket = bucket._parentBucket; }while (bucket != null); return(true); } break; } } return(false); //contact was not found or was not stale #endregion } } if ((leftBucket._bucketID & contact.NodeID) == leftBucket._bucketID) { currentBucket = leftBucket; } else { currentBucket = rightBucket; } } }