理论知识看完了,那就动手吧!
要想加入DHT网络,我们需要“介绍人” 带我们进去,就是初始节点,常用的初始节点有:
router.bittorrent.com:6881
router.utorrent.com:6881
还有两个我这边连不上就不提供了
消息交互用findnode来做示例:
然后对其发送find_node请求来获取其他节点
/**
* 对指定节点发送find_node请求
*
* @param node
*/
public void findNode(Node node) {
if (!node.check()) {
//node检查不通过
return;
}
//构建find_node消息体
FindNodeRequest findNodeRequest = new FindNodeRequest();
findNodeRequest.setId(localNode.getNodeId());
findNodeRequest.setTarget(GenerateUtils.generateNodeId());
RequestMessage findNodeMessage = new RequestMessage(findNodeRequest);
ThreadPool.findNodeSend.execute(new Runnable() {
@Override
public void run() {
//Bencode要发送的数据
byte[] data = bencode.encode(findNodeMessage);
//System.out.println(new String(data, Charset.forName("ISO-8859-1")));
try {
Thread.sleep(20);
//将消息放入发送列表中
sendTable.put(findNodeMessage.getT(), findNodeMessage.getQ());
datagramSocket.send(
new DatagramPacket(data, data.length, InetAddress.getByName(node.getIp()), node.getPort()));
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
我们发送findnode请求后等待对方回复,然后对其回复的消息进行逻辑处理
//监听消息
package vip.rory.dht.spider.service;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import vip.rory.bencode.Bencode;
import vip.rory.bencode.Type;
import vip.rory.dht.common.util.ByteUtils;
import vip.rory.dht.spider.config.ThreadPool;
import vip.rory.dht.spider.entity.SendTable;
import vip.rory.dht.spider.entity.message.body.FindNodeResponse;
import vip.rory.dht.spider.entity.message.body.GetPeersResponse;
import vip.rory.dht.spider.entity.message.body.PingResponse;
import vip.rory.dht.spider.enumerate.DhtMessageMethodEnum;
import vip.rory.dht.spider.enumerate.DhtMessageTypeEnum;
import vip.rory.dht.spider.enumerate.DhtParamEnum;
/**
* @author
* @date 2019年7月1日 下午3:09:35
*/
@Slf4j
@Service
public class MessageReceiver {
@Autowired
private DatagramSocket datagramSocket;
@Autowired
private Bencode bencode;
@Autowired
private MessageResponseService messageResponseService;
@Autowired
private MessageReceiveService messageReceiveService;
@Autowired
private SendTable sendTable;
public void listen() {
ThreadPool.messageReceive.execute(new Runnable() {
@Override
public void run() {
while (true) {
byte[] recvData = new byte[2048];
try {
DatagramPacket packet = new DatagramPacket(recvData, recvData.length);
datagramSocket.receive(packet);
messageAdapter(packet);
} catch (Exception e) {
//log.error(e.getMessage(), e);
}
}
}
});
}
private void messageAdapter(DatagramPacket packet) {
byte[] data = packet.getData();
Map decodeMap = null;
try {
decodeMap = bencode.decode(data, Type.DICTIONARY);
} catch (Exception e) {
//垃圾数据会导致解码异常
return;
}
//判断消息类型
DhtMessageTypeEnum dhtMessageTypeEnum = DhtMessageTypeEnum
.getEnumByCode((String) decodeMap.get(DhtParamEnum.TYPE.getCode()));
if (dhtMessageTypeEnum == null) {
//数据包异常
return;
}
switch (dhtMessageTypeEnum) {
case REQUEST:
handleRequest(packet, decodeMap);
break;
case RESPONSE:
handleResponse(packet, decodeMap);
break;
case ERROR:
//异常消息
break;
default:
//无效消息
break;
}
}
private void handleRequest(DatagramPacket packet, Map decodeMap) {
DhtMessageMethodEnum methodEnum = DhtMessageMethodEnum
.getEnumByCode((String) decodeMap.get(DhtParamEnum.METHOD.getCode()));
switch (methodEnum) {
case PING:
messageReceiveService.ping(packet, decodeMap);
break;
case FIND_NODE:
messageReceiveService.findNode(packet, decodeMap);
break;
case ANNOUNCE_PEER:
messageReceiveService.announcePeer(packet, decodeMap);
break;
case GET_PEERS:
messageReceiveService.getPeers(packet, decodeMap);
break;
default:
// TODO
break;
}
}
private void handleResponse(DatagramPacket packet, Map decodeMap) {
//响应消息--取出transactionId进行匹配
Object transactionId = decodeMap.get(DhtParamEnum.TRANSACTIONID.getCode());
//删除并取出
String methodCode = sendTable.remove(transactionId);
if (methodCode == null) {
//如果id不存在--手动解析
@SuppressWarnings("unchecked")
Map responseMap = (Map) decodeMap.get(DhtParamEnum.RESPONSE.getCode());
if (responseMap.get(GetPeersResponse.NODES) != null && responseMap.get(GetPeersResponse.TOKEN) != null
&& responseMap.get(GetPeersResponse.ID) != null) {
methodCode = DhtMessageMethodEnum.GET_PEERS.getCode();
} else if (responseMap.get(FindNodeResponse.ID) != null
&& responseMap.get(FindNodeResponse.NODES) != null) {
methodCode = DhtMessageMethodEnum.FIND_NODE.getCode();
} else if (responseMap.get(PingResponse.ID) != null) {
methodCode = DhtMessageMethodEnum.PING.getCode();
} else {
log.error("TransactionId不存在或已删除且无法匹配结果:{}", ByteUtils.bytes2String(packet.getData()));
return;
}
log.info("TransactionId不存在或已删除--匹配结果:{}", methodCode);
}
DhtMessageMethodEnum dhtMessageMethodEnum = DhtMessageMethodEnum.getEnumByCode(methodCode);
switch (dhtMessageMethodEnum) {
case PING:
messageResponseService.ping(packet, decodeMap);
break;
case FIND_NODE:
messageResponseService.findNode(packet, decodeMap);
break;
case ANNOUNCE_PEER:
// TODO
break;
case GET_PEERS:
// TODO
break;
default:
// TODO
break;
}
}
}
/**
* find_node响应 权重10
*
* @param packet
* @param decodeMap
*/
public void findNode(DatagramPacket packet, Map decodeMap) {
//解析数据
@SuppressWarnings("unchecked")
Map responseMap = (Map) decodeMap.get(DhtParamEnum.RESPONSE.getCode());
//将返回数据者加入我们自己的路由表中
Node node = new Node(packet.getAddress().getHostAddress(), packet.getPort(),
ByteUtils.string2Bytes(responseMap.get(FindNodeResponse.ID)), LocalDateTime.now(), 10).encode();
routingTable.add(node);
//解析其返回的nodes节点
String nodesStr = responseMap.get(FindNodeResponse.NODES);
List nodes = resolveNodes(nodesStr);
//对返回的nodes继续发送find_node请求
for (Node tempNode : nodes) {
messageSenderService.findNode(tempNode);
}
}
只需要对对应的请求做出对应的处理即可