package net.i2p.kademlia;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.Set;
import net.i2p.data.SimpleDataStructure;
/**
* Group, without inherent ordering, a set of keys a certain distance away from
* a local key, using XOR as the distance metric
*
* Refactored from net.i2p.router.networkdb.kademlia
* @since 0.9.2 in i2psnark, moved to core in 0.9.10
*/
public interface KBucket<T extends SimpleDataStructure> {
/**
* Lowest order high bit for difference keys.
* The lower-bounds distance of this bucket is 2**begin.
* If begin == 0, this is 43the closest bucket.
*/
public int getRangeBegin();
/**
* Highest high bit for the difference keys.
* The upper-bounds distance of this bucket is (2**(end+1)) - 1.
* If begin == end, the bucket cannot be split further.
* If end == (numbits - 1), this is the furthest bucket.
*/
public int getRangeEnd();
/**
* Number of keys already contained in this kbucket
*/
public int getKeyCount();
/**
* Add the peer to the bucket
*
* @return true if added
*/
public boolean add(T key);
/**
* Remove the key from the bucket
* @return true if the key existed in the bucket before removing it, else false
*/
public boolean remove(T key);
/**
* Update the last-changed timestamp to now.
*/
public void setLastChanged();
/**
* The last-changed timestamp
*/
public long getLastChanged();
/**
* Retrieve all routing table entries stored in the bucket
* @return set of Hash structures
*/
public Set<T> getEntries();
public void getEntries(SelectionCollector<T> collector);
public void clear();
}
首先,想要了解kbucket是什么意思需要了解kademilia算法,它是p2p数据库相关的算法,用来分布式存储一定的信息。想哟具体了解更多可以通过一下链接来了解kademilia算法、分布式存储
简单来讲就是用k-bucket来存储某些信息,这些信息可以理解成地址信息,每个k-bucket装的都是与本节点距离相等的节点的信息
这里的节点的距离的意思是,我们用一组01序列来表示节点信息,routerid,节点之间的距离是抑或距离 xor,这个是一个单独的异或程序来计算,下边是比较详细的解释
上文提到的距离,是学号(Node ID)之间的异或距离(XOR distance)。异或是针对yes/no或者二进制的运算.
异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1)
举2个例子:
01010000与01010010距离(即是2个ID的异或值)为00000010(换算为十进制即为2);
01000000与00000001距离为01000001(换算为十进制即为26+1,即65);
如此类推。
那通讯录是如何按距离分层呢?下面的示例会告诉你,按异或距离分层,基本上可以理解为按位数分层。设想以下情景:
以0000110为基础节点,如果一个节点的ID,前面所有位数都与它相同,只有最后1位不同,这样的节点只有1个——0000111,与基础节点的异或值为0000001,即距离为1;对于0000110而言,这样的节点归为“k-bucket 1”;
如果一个节点的ID,前面所有位数相同,从倒数第2位开始不同,这样的节点只有2个:0000101、0000100,与基础节点的异或值为0000011和0000010,即距离范围为3和2;对于0000110而言,这样的节点归为“k-bucket 2”;
……
如果一个节点的ID,前面所有位数相同,从倒数第n位开始不同,这样的节点只有2(i-1)个,与基础节点的距离范围为[2(i-1), 2i);对于0000110而言,这样的节点归为“k-bucket i”;
来源:作者:Li_Heng_lius
链接:https://www.jianshu.com/p/f2c31e632f1d
我们看一下这个类的定义,是 interface … 我们可以知道,这个KBucket是一个容器相关的interface,因为用到了泛型(泛型介绍),而泛型一般是为了容器而设计的,这里不赘述太多,而其容器的作用与我们介绍的KBucket的作用也相互印证
由于这个是泛型接口,那么对于一个接口,其所有变量和成员方法都应该是abstract的。这样我们就看一下他们的命名,对其功能有一个大致的了解,之后我们在KBucketLmpl,java中再分析
而对于这个T,它是一个泛型,泛型在这里可以解释为就是没有指定具体类型但是给出了范围的特定的类型,他就应该是KBucket其中的元素,包含了一个节点的相关的信息
package net.i2p.kademlia;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.Collections;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.ConcurrentHashSet;
/**
* A concurrent implementation using ConcurrentHashSet.
* The max size (K) may be temporarily exceeded due to concurrency,
* a pending split, or the behavior of the supplied trimmer,
* as explained below.
* The creator is responsible for splits.
*
* This class has no knowledge of the DHT base used for XORing,
* and thus there are no validity checks in add/remove.
*
* The begin and end values are immutable.
* All entries in this bucket will have at least one bit different
* from us in the range [begin, end] inclusive.
* Splits must be implemented by creating two new buckets
* and discarding this one.
*
* The keys are kept in a Set and are NOT sorted by last-seen.
* Per-key last-seen-time, failures, etc. must be tracked elsewhere.
*
* If this bucket is full (i.e. begin == end && size == max)
* then add() will call KBucketTrimmer.trim() do
* (possibly) remove older entries, and indicate whether
* to add the new entry. If the trimmer returns true without
* removing entries, this KBucket will exceed the max size.
*
* Refactored from net.i2p.router.networkdb.kademlia
* @since 0.9.2 in i2psnark, moved to core in 0.9.10
*/
class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> {
/**
* set of Hash objects for the peers in the kbucket
*/
private final Set<T> _entries;
/** include if any bits equal or higher to this bit (in big endian order) */
private final int _begin;
/** include if no bits higher than this bit (inclusive) are set */
private final int _end;
private final int _max;
private final KBucketTrimmer<T> _trimmer;
/** when did we last shake things up */
private long _lastChanged;
private final I2PAppContext _context;
/**
* All entries in this bucket will have at least one bit different
* from us in the range [begin, end] inclusive.
*/
public KBucketImpl(I2PAppContext context, int begin, int end, int max, KBucketTrimmer<T> trimmer) {
if (begin > end)
throw new IllegalArgumentException(begin + " > " + end);
_context = context;
_entries = new ConcurrentHashSet<T>(max + 4);
_begin = begin;
_end = end;
_max = max;
_trimmer = trimmer;
}
public int getRangeBegin() { return _begin; }
public int getRangeEnd() { return _end; }
public int getKeyCount() {
return _entries.size();
}
/**
* @return an unmodifiable view; not a copy
*/
public Set<T> getEntries() {
return Collections.unmodifiableSet(_entries);
}
public void getEntries(SelectionCollector<T> collector) {
for (T h : _entries) {
collector.add(h);
}
}
public void clear() {
_entries.clear();
}
/**
* Sets last-changed if rv is true OR if the peer is already present.
* Calls the trimmer if begin == end and we are full.
* If begin != end then add it and caller must do bucket splitting.
* @return true if added
*/
public boolean add(T peer) {
if (_begin != _end || _entries.size() < _max ||
_entries.contains(peer) || _trimmer.trim(this, peer)) {
// do this even if already contains, to call setLastChanged()
boolean rv = _entries.add(peer);
setLastChanged();
return rv;
}
return false;
}
/**
* @return if removed. Does NOT set lastChanged.
*/
public boolean remove(T peer) {
boolean rv = _entries.remove(peer);
//if (rv)
// setLastChanged();
return rv;
}
/**
* Update the last-changed timestamp to now.
*/
public void setLastChanged() {
_lastChanged = _context.clock().now();
}
/**
* The last-changed timestamp, which actually indicates last-added or last-seen.
*/
public long getLastChanged() {
return _lastChanged;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(1024);
buf.append(_entries.size());
buf.append(" entries in (").append(_begin).append(',').append(_end);
buf.append(") : ").append(_entries.toString());
return buf.toString();
}
}
涉及的信息
记这一个文件的笔记的时候很纠结,一是本身就对这个KBucketImpl的代码看过一遍,另外是对kademila算法有一定的了解,而且这个文件写的代码并不复杂,但是因为其中有几个点我看不明白,但是想了半天没看懂,还是先留着这些问题,之后再补上。
上边我们记录来KBucket.java文件,这个文件是一个泛型类,是一个容器,一个接口,而这里我们记录的KBucketImpl。从命名上我们就能看明白这个是KBucket 类的实现.
我们通过对Kademila算法的了解知道,Kbucket是一个用来装节点信息的bucket,本来只需要一个节点信息就好了,但是我们为了增加安全性和稳定性,我们用了多个节点,所以KBucket本身是个容器,这个是通过set来完成的,是一个无序不重复的集合。
class KBucketImpl<T extends SimpleDataStructure> implements KBucket<T> {
/**
* set of Hash objects for the peers in the kbucket
*/
private final Set<T> _entries;
/** include if any bits equal or higher to this bit (in big endian order) */
private final int _begin;
/** include if no bits higher than this bit (inclusive) are set */
private final int _end;
private final int _max;
private final KBucketTrimmer<T> _trimmer;
/** when did we last shake things up */
private long _lastChanged;
private final I2PAppContext _context;