[搜片神器]直接从DHT网络下载BT种子的方法

简介: 转自:http://www.cnblogs.com/miao31/p/3332819.html   DHT抓取程序开源地址:https://github.com/h31h31/H31DHTDEMO 数据处理程序开源地址:https://github.

 

转自:http://www.cnblogs.com/miao31/p/3332819.html

 

DHT抓取程序开源地址:https://github.com/h31h31/H31DHTDEMO

数据处理程序开源地址:https://github.com/h31h31/H31DHTMgr

DHT系列文章:

1.[搜片神器] 之P2P中DHT网络爬虫原理

2.[搜片神器]之DHT网络爬虫的代码实现方法

3.[搜片神器]之DHT网络爬虫的C++程序初步开源

--------------------------------------------------------------------------------------------------------------------

看懂此文章需要提前看明白上面的系列文章,还需要你有TCP网络编程和bencode编码方法基础上,如果都看不明白,可以到娱乐区http://h31bt.com 去看看,休息下...

 

在介绍了这么多期文章后,最后介绍BT网络里面一个比较重要种子下载协议,方便大家知道如何从DHT网络直接下载种子的问题.

先说下我们目前下载电影等文件是如何下载的,比如我们有个BT种子,就可以去下载对应的文件,但如果我们只有个文件名字,如何去找BT种子呢?

首先我们可以去 http://h31bt.com 去通过搜索得到磁连接,然后就由此字符串去下载对应的种子文件和电影等信息,但如果没有网站让你下载种子,我们又当如何去搜索这个种子呢?

目前我们下载BT种子有两种方式:                                                                           

  1. 通过HTTP直接从WEB服务器上下载,这种直接方便,比如从迅雷服务器上下载种子,
  2. 再就是通过BT软件从网络里面去获取BT网络里面专门有个下载种子的协议文件,只能下载种子,然后种子下载好后就可以交给BT软件来下载数据了.

如何从DHT网络下载种子,必须先看两个协议文章:

http://www.bittorrent.org/beps/bep_0009.html

http://www.bittorrent.org/beps/bep_0010.html

这里面有介绍,但还是需要说明一下如何操作的流程方便大家更好的理解.

 我们的代码流程必须还是基于 DHT抓取程序开源地址:https://github.com/h31h31/H31DHTDEMO 之上,因为是从DHT网络里面获取数据,

需要我们在此之上操作后续流程.

之前的DHT有SEARCH的相关代码来搜索这个HASH对应的哪些IP在提供下载.

复制代码
        /* This is how you trigger a search for a torrent hash.  If port (the second argument) is non-zero, it also performs an announce.
           Since peers expire announced data after 30 minutes, it's a good idea to reannounce every 28 minutes or so. */
        if(searching) {
            //m_dht.dht_random_bytes((void*)hashList[2],20);
            if(m_soListen >= 0)
                m_dht.dht_search(hashList[2], 0, AF_INET, DHT_callback, this);
            if(s6 >= 0)
                m_dht.dht_search(hashList[2], 0, AF_INET6, DHT_callback, this);
            searching = 0;
        }
复制代码

搜索到对方返回的IP信息和端口号后,大家可以分析dht.c里面的函数代码dht_periodic(const void *buf, size_t buflen,const struct sockaddr *fromAddr, int fromlen,time_t *tosleep,dht_callback *callback, void *closure)函数里面的ANNOUNCE_PEER返回请求里面带有对方表明自己此BT种子对应的认证码peerid.

复制代码
dht_periodic(const void *buf, size_t buflen,const struct sockaddr *fromAddr, int fromlen,time_t *tosleep,dht_callback *callback, void *closure)
函数里面的ANNOUNCE_PEER

        case ANNOUNCE_PEER:
            _dout("Announce peer!From IP:%s:%d\n",inet_ntoa(tempip->sin_addr),tempip->sin_port);
            new_node(id, fromAddr, fromlen, 1);

            if(id_cmp(info_hash, zeroes) == 0) 
            {
                _dout("Announce_peer with no info_hash.\n");
                send_error(fromAddr, fromlen, tid, tid_len,203, "Announce_peer with no info_hash");
                break;
            }
            if(!token_match(token, token_len, fromAddr)) {
                _dout("Incorrect token for announce_peer.\n");
                send_error(fromAddr, fromlen, tid, tid_len,203, "Announce_peer with wrong token");
                break;
            }
            if(port == 0) {
                _dout("Announce_peer with forbidden port %d.\n", port);
                send_error(fromAddr, fromlen, tid, tid_len,203, "Announce_peer with forbidden port number");
                break;
            }
            if(callback) 
            {
                (*callback)(closure, DHT_EVENT_ANNOUNCE_PEER_VALUES, info_hash,(void *)fromAddr, port,id);//此ID就是peerid,
            }
复制代码

知道了对应的IP,端口号,还有种子ID号,就可以向对方发送请求了.

获取HASH是通过UDP网络,但下载BT种子是通过TCP来处理,相当于别人是TCP服务器,我们连接过去,直接下载对应PEERID的种子就行了.

BT种子在DHT网络下载流程                                                                  

 先看http://www.bittorrent.org/beps/bep_0010.html协议介绍,我们必须先握手  

 

 此包构造比较简单,按照格式进行组装就行了,然后发送出去,对方就会回应自己是什么客户端的软件提供种子下载.

?
void  CH31BTMgr::Encode_handshake()
{
     //a byte with value 19 (the length of the string that follows);
     //the UTF-8 string "BitTorrent protocol" (which is the same as in ASCII);
     //eight reserved bytes used to mark extensions;
     //the 20 bytes of the torrent info hash;
     //the 20 bytes of the peer ID.
     char  btname[256];
     memset (btname,0, sizeof (btname));
     sprintf (btname, "BitTorrent protocol" );
     char  msg[1280];
     memset (msg,0, sizeof (msg));
     msg[0]=19;
     memcpy (&msg[1],btname,19);
     char  ext[8];
     memset (ext,0, sizeof (ext));
     ext[5]=0x10;
 
     memcpy (&msg[20],ext,8);
     memcpy (&msg[28],m_hash,20);
     memcpy (&msg[48],m_peer_id,20);
     int  res1=Write(msg, 68); //TCP发送消息
}

在发送握手后,我们可以接着发送种子数据请求包,需要学习http://www.bittorrent.org/beps/bep_0009.html 里面的内容:  

复制代码
extension header
The metadata extension uses the extension protocol (specified in BEP 0010 ) to advertize its existence. It adds the "ut_metadata" entry to the "m" dictionary in the extension header hand-shake message. This identifies the message code used for this message. It also adds "metadata_size" to the handshake message (not the "m" dictionary) specifying an integer value of the number of bytes of the metadata.

Example extension handshake message:

{'m': {'ut_metadata', 3}, 'metadata_size': 31235}
extension message
The extension messages are bencoded. There are 3 different kinds of messages:

1 request 
2 data 
3 reject 
The bencoded messages have a key "msg_type" which value is an integer corresponding to the type of message. They also have a key "piece", which indicates which part of the metadata this message refers to.

In order to support future extensability, an unrecognized message ID MUST be ignored.
复制代码

这就需要会bencode的相关代码,这个大家可以网上搜索进行编译,如果实现搞不定,可以留下邮箱我将此类代码发送给你,其实也是网上收集整理的.

复制代码
void CH31BTMgr::Encode_Ext_handshake()
{
    entry m;
    m["ut_metadata"] = 0;
    entry e;
    e["m"]=m;

    char msg[200];
    char* header = msg;
    char* p = &msg[6];
    int len = bencode(p, e);
    int total_size = 2 + len;
    namespace io = detail;
    io::write_uint32(total_size, header);
    io::write_uint8(20, header);
    io::write_uint8(0, header);

    int res1=Write(msg, len + 6);
}
复制代码

如果别人回应的是3,那就直接退出吧,说明别人拒绝了你.

如果回应是2,则返回的是数据区,每块是16K大小,最后一包不是.

 

复制代码
data
The data message adds another entry to the dictionary, "total_size". This key has the same semantics as the "metadata_size" in the extension header. This is an integer.

The metadata piece is appended to the bencoded dictionary, it is not a part of the dictionary, but it is a part of the message (the length prefix MUST include it).

If the piece is the last piece of the metadata, it may be less than 16kiB. If it is not the last piece of the metadata, it MUST be 16kiB.

Example:

{'msg_type': 1, 'piece': 0, 'total_size': 3425}
d8:msg_typei1e5:piecei0e10:total_sizei34256eexxxxxxxx...
The x represents binary data (the metadata).
复制代码

下面给出如何进行提交我需要第几包的数据代码:

复制代码
void CH31BTMgr::write_metadata_packet(int type, int piece)
{
    ASSERT(type >= 0 && type <= 2);
    ASSERT(piece >= 0);

    entry e;
    e["msg_type"] = type;
    e["piece"] = piece;

    char const* metadata = 0;
    int metadata_piece_size = 0;

    if (type == 1)
    {
        e["total_size"] = 14132;
        int offset = piece * 16 * 1024;
        //metadata = m_tp.metadata().begin + offset;
        metadata_piece_size = (std::min)(int(14132 - offset), 16 * 1024);
    }

    char msg[200];
    char* header = msg;
    char* p = &msg[6];
    int len = bencode(p, e);
    int total_size = 2 + len + metadata_piece_size;
    namespace io = detail;
    io::write_uint32(total_size, header);
    io::write_uint8(20, header);
    io::write_uint8(m_message_index, header);

    int res1=Write(msg, len + 6);
}
复制代码

在接收到一包请求后我们才可以继续下一包的请求,下面给了我们如何解析这一包的问题代码:

复制代码
// 处理一个完整的包数据
bool CH31BTMgr::DeCodeFrameData(char * buffer,int buflen)
{
    char * p = (char *)mhFindstr(buffer, buflen, "ut_metadatai", 12);
    if(p) 
    {
        m_message_index=atoi(&p[12]);
        if(m_message_index==2)
        {
            return false;
        }
        write_metadata_packet(0,0);
        char filename[256];
        memset(filename,0,sizeof(filename));
        sprintf(filename,"%s\\torrent.txt",m_workPath);
        DelFile(filename);
    } 

    p = (char *)mhFindstr(buffer, buflen, "metadata_sizei", 14);
    if(p) 
    {
        m_metadata_size=atoi(&p[14]);
        m_fileCnt=(int)(m_metadata_size/16384)+1;
    } 

    p = (char *)mhFindstr(buffer, buflen, "msg_typei", 9);
    if(p) 
    {
        int type1=atoi(&p[9]);
        if(type1==1)
        {
            p = (char *)mhFindstr(buffer, buflen, "piecei", 6);
            if(p) 
            {
                int piece=atoi(&p[6]);
                p = (char *)mhFindstr(buffer, buflen, "total_sizei", 11);
                if(p) 
                {
                    int total_size=atoi(&p[11]);
                    p = (char *)mhFindstr(buffer, buflen, "ee", 2);
                    if(p) 
                    {
                        //保存数据
                        FILE* pfile=NULL;
                        char filename[256];

                        memset(filename,0,sizeof(filename));
                        sprintf(filename,"%s\\torrent.txt",m_workPath);
                        char openmethod[5]="a";
                        if(piece==0)
                            sprintf(openmethod,"w");
                        if((pfile=fopen(filename,openmethod))!=NULL)
                        {
                            if((piece+1)*16*1024<total_size)
                            {
                                fseek(pfile,(piece)*16*1024,SEEK_SET);
                                fwrite(&p[2],1,16*1024,pfile);
                                write_metadata_packet(0,piece+1);
                                fclose(pfile);
                            }
                            else
                            {
                                fwrite(&p[2],1,total_size-(piece)*16*1024,pfile);
                                fclose(pfile);
                                ManageTorrentFileToRealFile(filename);
                            }
                        }
                    }
                }
            }
        }
        else if(type1==2)
        {
            return false;
        }
    } 
    
    return true;
}

void * mhFindstr(const void *haystack, size_t haystacklen,const void *needle, size_t needlelen)
{
const char *h =(const char *) haystack;
const char *n =(const char *) needle;
size_t i;

 
 

/* size_t is unsigned */
if(needlelen > haystacklen)
return NULL;

 
 

for(i = 0; i <= haystacklen - needlelen; i++) {
if(memcmp(h + i, n, needlelen) == 0)
return (void*)(h + i);
}
return NULL;
}

 
复制代码

 

接下来说下如何进行快速调试的问题:                                                                                        

       第一次调试也很天真的等着DHT网络上的数据过来,需要等很久,而且调试总是发现别人不回应,要么就是拒绝,经过一段时间后,

问朋友总是不对问题,结果是协议没有构造对.下面就需要注意的地方总结下:

1.一定要接收到别的人PEERID后才能够与别人交流,不然别人肯定不理你;

2.构造协议调试不能够在外网络上调试,最好大家将mono-monotorrent源代码下载回来,调试分析下,本地开启服务器;

3.通过本地与mono-monotorrent进行调试,你就可以分析出是哪里不对的问题,是不是协议哪些封装得不对的问题.

4.通过DHT网络下载回来的种子肯定是最新的,WEB下载的可能还没有呢..

5.通过协议下载回来的种子好像没有announce-list,不知道为什么不提供一些内容,可能还有些什么关键地方没有下载,分析mono-monotorrent代码里面就是不提供下载,希望高手指点.

6.TCPClient接收数据区需要开到16K以上,这样方便处理,当然如果会前后拼接包就更好.

7.如果需要bencode相关的编码C++代码,可以在此留言或者给h31h31#163.com发邮件.

 

如果此文章看不太明白,请先看看之前的文章,分析调试下代码,再来学习此文章可能就比较懂一些.

希望有了解的朋友更好的交流和进步.在此留言学习讨论.

大家看累了,就移步到娱乐区http://h31bt.com 去看看,休息下...

img_e00999465d1c2c1b02df587a3ec9c13d.jpg
微信公众号: 猿人谷
如果您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】
如果您希望与我交流互动,欢迎关注微信公众号
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

相关文章
|
机器学习/深度学习 算法 调度
14种智能算法优化BP神经网络(14种方法)实现数据预测分类研究(Matlab代码实现)
14种智能算法优化BP神经网络(14种方法)实现数据预测分类研究(Matlab代码实现)
774 0
|
12月前
|
机器学习/深度学习 数据采集 边缘计算
基于灰色神经网络的预测方法
基于灰色神经网络的预测方法
618 0
|
监控 安全 网络安全
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
3174 89
|
存储 Linux 容器
【Container App】在容器中抓取网络包的方法
本文介绍在Azure Container App中安装tcpdump抓取网络包,并通过Storage Account上传抓包文件的方法。内容包括使用curl和nc测试外部接口连通性、长Ping端口、安装tcpdump、抓取网络包、以及通过crul命令上传文件至Azure Storage。适用于需要分析网络请求和排查网络问题的场景。
388 0
|
算法 Python
【EI复现】考虑网络动态重构的分布式电源选址定容优化方法(Matlab代码实现)
【EI复现】考虑网络动态重构的分布式电源选址定容优化方法(Matlab代码实现)
472 0
|
机器学习/深度学习 数据采集 TensorFlow
基于CNN-GRU-Attention混合神经网络的负荷预测方法(Python代码实现)
基于CNN-GRU-Attention混合神经网络的负荷预测方法(Python代码实现)
711 0
|
机器学习/深度学习 边缘计算 算法
基于BP神经网络的电池容量预测方法研究
基于BP神经网络的电池容量预测方法研究
计算网络号的直接方法
子网掩码用于区分IP地址中的网络部分和主机部分,连续的“1”表示网络位,“0”表示主机位。例如,255.255.255.0 的二进制为 11111111.11111111.11111111.00000000,前24位是网络部分。通过子网掩码可提取网络号,如 IP 192.168.1.10 与子网掩码 255.255.255.0 的网络号为 192.168.1.0。此外,文档还介绍了十进制与二进制间的转换方法,帮助理解IP地址的组成与计算。
1029 11
|
机器学习/深度学习 数据采集 人工智能
GeneralDyG:南洋理工推出通用动态图异常检测方法,支持社交网络、电商和网络安全
GeneralDyG 是南洋理工大学推出的通用动态图异常检测方法,通过时间 ego-graph 采样、图神经网络和时间感知 Transformer 模块,有效应对数据多样性、动态特征捕捉和计算成本高等挑战。
609 18
GeneralDyG:南洋理工推出通用动态图异常检测方法,支持社交网络、电商和网络安全
|
缓存 数据中心 网络架构
5个减少网络延迟的简单方法
高速互联网对工作与娱乐至关重要,延迟和断线会严重影响效率和体验。本文探讨了导致连接缓慢的三个关键因素:吞吐量、带宽和延迟,并提供了减少延迟的实用方法。包括重启设备、关闭占用带宽的程序、使用有线连接、优化数据中心位置以及添加内容分发网络 (CDN) 等策略。虽然完全消除延迟不可能,但通过这些方法可显著改善网络性能。
4856 7

热门文章

最新文章