【JAVA】从0开始写DHT/磁力爬虫 03 HelloWorld

理论知识看完了,那就动手吧!

 

要想加入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);
        }
    }

 

 

只需要对对应的请求做出对应的处理即可

你可能感兴趣的:(从0开始写DHT/磁力爬虫)