dht協議實現
上一篇完成了路由表的實現,建立了路由表后,我們還要對路由表進行初始化,因為一開始路由表為空,所以我們需要借助一些知名的dht網絡中的節點,對這些節點進行find_node,然后一步步初始化路由表。這里我們選dht.transmissionbt.com來進行初始化。在dht網絡都是使用upd協議進行數據的傳輸,所以我們需要開啟一個upd端口,這里使用netty來做通信框架。這里我們主要來看怎么發送find_node
/**
* find_node
* @param node 自己的id
* @param target 目標id
* @param address 請求地址
* @param num channel號
*/
public void findNode(String node,String target,InetSocketAddress address,int num){
if(!channels.get(num).isWritable()){
return;
}
FindNodeRequest findNodeRequest=new FindNodeRequest(node,target);
channels.get(num).writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(bencode.encode(DHTUtil.beanToMap(findNodeRequest))), address));
}
FindNodeRequest 主要有哪些字段
@Data
@AllArgsConstructor()
public class BaseRequest {
private String t;//messageid 2 byte
private String y;//"q" for query, "r" for response, or "e" for error
private String q;//method ping/find_node/get_peers/announce_peer
//private String v;//client identifier registered in BEP 20 這里不管這個
}
public class FindNodeRequest extends BaseRequest{
private FindNodeRequestContent a=new FindNodeRequestContent();
@Data
@Accessors(chain = true)
private static class FindNodeRequestContent{
private String id;
private String target;
}
public FindNodeRequest(String id,String target){
super(DHTUtil.generateMessageId(),
MessageTypeEnmu.QUERY.getKey(),
MethodEnmu.FIND_NODE.getKey());
a.id=id;
a.target=target;
}
public static void main(String[] args) {
FindNodeRequest f=new FindNodeRequest("df","23");
}
}
find_node返回結果后,會對其進行bencoded解碼最后解析成ProcessDto對象,接下來我們來看怎么處理返回結果
@Override
public void activeProcess(ProcessDto processDto) {
Map<String, Object> rMap = DHTUtil.getParamMap(processDto.getRawMap(), "r", "FIND_NODE,找不到r參數.map:" + processDto.getRawMap());
List<Node> nodeList = DHTUtil.getNodeListByRMap(rMap);
//為空退出
if (CollectionUtils.isEmpty(nodeList)) return;
//去重
Node[] nodes = nodeList.stream().distinct().toArray(Node[]::new);
//將nodes加入發送隊列繼續find_node
for (Node node : nodes) {
FileUtil.wirteNode("nodes1=="+node.getIp()+","+node.getPort()+"\r\n");
findNodeTask.put(node.toAddress());
}
byte[] id = DHTUtil.getParamString(rMap, "id", "FIND_NODE,找不到id參數.map:" + processDto.getRawMap()).getBytes(CharsetUtil.ISO_8859_1);
//將發送消息的節點加入路由表
routingTables.get(processDto.getNum()).put(new Node(id, processDto.getSender(), NodeRankEnum.FIND_NODE_RECEIVE.getKey()));
}
建立路由表的進行find_node的時候,對方也會保存我們的信息到他們的路由表所以我們只需要實現ping用來回應自己一直在線。最后獲取infohash最主要的是
get_peers和announce_peer分別對應GetPeersRequest和AnnoucePeersRequest,發送方式也處理方式也類似,可以參考一下源碼https://github.com/mistletoe9527/dht-spider
好了,現在已經可以通過dht網絡獲取到infohash了,但是只有infohash還不夠,因為還需要獲取種子的metadata,這樣才能知道這個infohash對應的種子所對應的文件。只有建立起文件和infohash的對應關系,我們才能提供搜索,下一篇會介紹怎么從dht網絡中獲取種子的infohash。