DHT协议C++实现过程中各种问题

---恢复内容开始---

博主是个菜鸟,什么地方写的不对或者理解有误的地方还请指正出来。

DHT协议是BT协议中的一部分,也是一个辅助性的协议。HTTP协议中用

来定位资源也就是html文本是用URL这样的协议的,而在BT或者说P2P的

世界中,没有了以前那样可以直接定位的服务器,所以需要能够动态的掌

握到资源的分布,那DHT协议就是BT中用来定位资源的协议,具体的不多

说,可以看看官方网站对于BT或者DHT十分详尽的描述:

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

或者去看看其他人翻译出来的文章理解理解:

http://blog.csdn.net/xxxxxx91116/article/details/7970815

总之,实现DHT协议是BT的前提,是为了能够找到infohash这样一个种子用

hash算法产生的20个字符的字符串,也是找到种子的前提。

我们知道任何应该架构于网络的应用程序,只要是使用的TCP/IP协议的,

必然是基于第三层的IP协议,和第四层的TCP或者UDP协议。当然,我们所说

的驱动级网络编程不在此列。那对于使用UDP传输数据的DHT协议来说,必然

需要对传输或者说交流的数据进行编码,这个编码就是B编码,我找到一个C#

写出这个编码的代码:

http://www.cnblogs.com/technology/p/BEncoding.html,稍加改动成

C++的B编码程序,可以跑起来,但是有一个致命的问题存在,容后再说,先贴

代码:

 1 #define USING
 2 #include "DataStruction.h"
 3 #include <string>
 4 using namespace std;
 5
 6     /*
 7     https://github.com/CreateChen/Bencode
 8     transfer the CreateChen‘s c# code into c++, the thought is very impressive!
 9     */
10
11
12 enum EncodeState
13 {
14     KEY,
15     VALUE,
16 };
17
18 class BCode
19 {
20 private:
21     static int index;
22     static BaseData* RealDecodeToDic(string str, int& index, EncodeState state);24 public:
25     static BaseData* DecodeToDic(string str);27     static string EncodeToStr(BaseData*);
28 };
#include "BCode.h"
#include <sstream>

int BCode::index = 0;

//refactoring! to support the users a simple interface, to delegate the real working function inner the interface!
BaseData* BCode::DecodeToDic(string str)
{
    return RealDecodeToDic(str, BCode::index, VALUE);
}

//the recursion is for analyzing the dictionary!
//it must can be used in other place!!
BaseData* BCode::RealDecodeToDic(string str, int& index, EncodeState state)
{
    DicData* dicData = new DicData();
    char c = str[index];

    while( c != ‘e‘)
    {
        if(c == ‘d‘)
        {
            index ++;
            return RealDecodeToDic(str, index, KEY);
        }
        if(c == ‘i‘)
        {
            string returnStr = "";
            index ++;
            //c = str[index];
            while(str[index] != ‘e‘)
            {
                returnStr += str[index];
                index++;
                //c = str[index];
            }
            //transfer string to char, then transfer char to int
            IntData * intData = new IntData();
            //int returnInt = atoi(returnStr.c_str());
            intData->SetValue(atoi(returnStr.c_str()));
            return intData;
        }
        if(c == ‘l‘)
        {
            index++;
            ListData* listData = new ListData();
            while (str[index] != ‘e‘)
            {
                listData->add(RealDecodeToDic(str, index, VALUE));
                index++;
            }
            return listData;
        }
        if(‘0‘ < c && c <= ‘9‘)
        {
            string returnString = "";
            string contentString = "";
            while(str[index] != ‘:‘)
            {
                returnString += str[index];
                index++;
            }
            int stringLength = atoi(returnString.c_str());
            for (int i = 0; i < stringLength; i++)
            {
                contentString += str[index + 1];
                index++;
            }

            if(state == VALUE)
            {
                StrData* strData = new StrData();
                strData->SetValue(contentString);
                return strData;
            }
            index++;
            dicData->add(contentString, RealDecodeToDic(str, index, VALUE));
            state = KEY;
            index++;
        }
        c = str[index];
    }
    return dicData;
}

//a kind of recursion! it‘s smart!
string BCode::EncodeToStr(BaseData* baseData)
{
    string newString;
    if(baseData->GetDataType() == B_DIC)
    {
        DicData* dicData = static_cast<DicData*>(baseData);
        map<string, BaseData*> newMap = dicData->GetValue();
        newString.append("d");

        for(map<string, BaseData*>::iterator it = newMap.begin(); it != newMap.end(); it++)
        {
            stringstream ss;
            ss << (it->first).length();
            newString = newString.append(ss.str() )+ ":" + it->first;

            BaseData* recursionBaseData = it->second;
            newString.append(EncodeToStr(recursionBaseData));
        }
        newString.append("e");
    }

    if(baseData->GetDataType() == B_INT)
    {
        IntData* intData = static_cast<IntData*>(baseData);
        //int iValue = intData->GetValue();
        stringstream ss;
        ss << (intData->GetValue());
        newString = "i" + newString.append(ss.str()) + "e";
    }

    //can‘t reach here
    //use assert!
    if (baseData->GetDataType() == B_LIST)
    {
        newString.append("l");
        ListData* listData = static_cast<ListData*>(baseData);
        list<BaseData*> newList = listData->GetValue();
        for(list<BaseData*>::iterator it = newList.begin(); it != newList.end(); it++)
        {
            newString.append(EncodeToStr(*it));
        }
        newString.append("e");
    }

    if (baseData->GetDataType() == B_STR)
    {
        StrData* strData = static_cast<StrData*>(baseData);
        stringstream ss;
        ss << (strData->GetValue()).length();
        //string newStr = strData->GetValue();
        newString = newString + ss.str() + ":" + (strData->GetValue());
    }

    return newString;
}

当然好的B编码必然对应好的数据结构,因为不管网络中传输的数据是什么样子的,

我们必须要在自己的程序内管理好数据结构才行,因为我们要在map中放入还不能

确定数据类型的数据,所以需要在map中放入父类实例的指针,以让我们能够在以后

还能通过指针使用多态的特性让子类去代替父类,看看代码理解下,当然这里使用了

几个面向对象语言的高级特性:设计模式中的composit模式,STL以及多态。大家

可以去了解下,当然这部分的代码绝大部分是我群里的“元古”大神所实现的数据结构。

代码和类图都在下面贴出来:

#ifndef  USEING
#define USEING

#include <string>
#include <list>
#include <map>
#include<assert.h>
#include <iostream>
#include <set>
using namespace std;

typedef enum BeType
{
    B_STR,
    B_INT,
    B_LIST,
    B_DIC,
}BeType;

class BaseData
{
public:
    BeType beType;

public:
    //stick to polymorphisms
    //virtual ~BaseData();
    virtual BeType GetDataType()
    {
        return beType;
    };
    virtual void SetDataType(BeType beType){};

};

class StrData : public BaseData
{
private:
    string sValue;
public:
    StrData():sValue("")
    {
        SetDataType(B_STR);
    };
    BeType GetDataType()
    {
        return beType;
    }
    void SetDataType(BeType newBeType)
    {
        beType = newBeType;
    }

    string GetValue() {return sValue; };
    void SetValue(char byte) {sValue.append(&byte, 1); };
    void SetValue(char* bytes)
    {
        int length = sizeof(bytes);
        sValue.append(bytes, length);
    };
    void SetValue(string str)
    {
        sValue.append(str);
    };
};

class IntData : public BaseData
{
private:
    int iValue;
public:
    IntData():iValue(0)
    {
        SetDataType(B_INT);
    }
    BeType GetDataType()
    {
        return beType;
    }
    void SetDataType(BeType newBeType)
    {
        beType = newBeType;
    }

    int GetValue()
    {
        return iValue;
    }
    void SetValue(char* numStr)
    {
        iValue = atoi(numStr);
    }
    void SetValue(int num)
    {
        iValue = num;
    }
};

class ListData : public BaseData
{
private:
    //there is a strong relationship between ListData and BaseData, it is named "compose" , it is also a design pattern!
    list<BaseData*> lValue;
public:
    ListData():lValue(0)
    {
        SetDataType(B_LIST);
    };
    BeType GetDataType()
    {
        return beType;
    }
    void SetDataType(BeType newBeType)
    {
        beType = newBeType;
    }

    list<BaseData*> GetValue()
    {
        return lValue;
    }
    void add(BaseData* baseData)
    {
        lValue.push_back(baseData);
    }
    //destructor! it will be called when user use the "delete" key, and inner ListData , BaseData will also call delete!
    ~ListData()
    {
        for(list<BaseData*>::iterator it = lValue.begin(); it != lValue.end(); it++)
        {
            if(*it != NULL)
                delete *it;
        }
        lValue.clear();
    }
};

class DicData : public BaseData
{
private:
    //the reason to use the pointer,because we need to transfer the father to the son, it is polymorphisms
    //remember
    map<string, BaseData*> dValue;

public:
    //no initialize for map
    DicData()
    {
        SetDataType(B_DIC);
    };
    BeType GetDataType()
    {
        return beType;
    }
    void SetDataType(BeType newBeType)
    {
        beType = newBeType;
    }

    map<string, BaseData*> GetValue()
    {
        return dValue;
    }
   //except the second value, we can also insert some else value like string to StrData, int to IntData, list to ListData!
    void add(string str, BaseData* baseData)
    {
        dValue.insert(make_pair(str, baseData));
    }
    void add(char* bytes, BaseData* baseData)
    {
        dValue.insert(make_pair(bytes, baseData));
    }
    void add(string strOne, string strTwo)
    {
        StrData* strData = new StrData();
        strData->SetValue(strTwo);
        dValue.insert(make_pair(strOne, strData));
    }
    void add(string str, char* bytes, int lengh)
    {
        string tempStr = "";
        tempStr.append(bytes, lengh);
        add(str, tempStr);
    }
    ~DicData()
    {
        for(map<string, BaseData*>::iterator it = dValue.begin(); it != dValue.end(); it++)
        {
            //attention the different between the map and list
            //the iterator is a pointer, but we‘ve used the ->
            if(it->second != NULL)
                delete it->second;
        }
        dValue.clear();
    }
};
#endif

类图:

到此处为止,除了协议本身的实现没有贴出来,最终的辅助程序都贴出来了。

至于peer之间交互信息的代码就不贴出来,有了几个重要的辅助程序,基本上

稍微写写大概也能写出来。

然后我就开始跑我的DHT爬虫,每次跑起来总是等不到我想要的infohash,

我从头到位debug了下,发现我是能够接收到数据的,但是数据总是不能被我的

BCode正确解码,我跟踪了下接受数据,发现了这个状况:

这里只取出来了部分数据,里面不仅有负值的ASC2数据,还有‘\0‘!!

于是cout出来呈现这样:

我的代码中是要把char*赋值给string的,结果竟然有‘\0‘!并且‘\0‘就是协议本身允许的能够传输的数据!

在赋值中直接就把我网络字节流给截断了!我惊觉我的所有BCode都不能再用了,因为统统都是string构成的

基本元素!不仅如此,我还意识到传输中一大堆负值是怎么回事??

于是我找了个能跑的DHT爬虫,也一步步跟踪了下,发现ASC2的负值都是能够传输的,也是符合协议的,

最奇葩的是竟然还能被解析成功,这样的数据打印都打印不出来啊...太坑了,不过也扩展了见识,以下是对比图:

现在终于发现不能解析的原因了,而许多全面向对象的语言就不存在此问题,是因为像c#、Java或者

Python这样的语言中的string根本就不是默认‘\0‘为结尾的...

所以现在需要改下我BCode中string部分,全部替换为char*,可惜了如此漂亮的递归啊!

---恢复内容结束---

时间: 2024-10-11 17:15:40

DHT协议C++实现过程中各种问题的相关文章

ios 协议(delegate)使用过程中遇到assign attribute must be unsafeunretained

今天在使用协议的过程中,偶然发现这样使用 @interface AppDelegate (){ id<ChatDelegate> testdelegate; } @property (nonatomic , assign) id<ChatDelegate> testdelegate; @end @implementation AppDelegate @synthesize testdelegate; 会报错: Existing instance variable 'delegate

BitTorrent DHT 协议中文翻译

前言 做了一个磁力链接和BT种子的搜索引擎 {Magnet & Torrent},因此把 DHT 协议重新看了一遍. 原文:DHT Protocol 译文:BitTorrent DHT 协议中文翻译 BitTorrent 使用"分布式哈希表"(DHT)来为无 tracker 的种子(torrents)存储 peer 之间的联系信息.这样每个 peer 都成了 tracker.这个协议基于 Kademila[1] 网络并且在 UDP 上实现. 请注意本文档中使用的术语,以免混乱.

使用beanstalkd实现定制化持续集成过程中pipeline - 持续集成系列

持续集成是一种项目管理和流程模型,依赖于团队中各个角色的配合.各个角色的意识和配合不是一朝一夕能练就的,我们的工作只是提供一种方案和能力,这就是持续集成能力的服务化.而在做持续集成能力服务化的过程中,最核心的一点就是,如何实现一个可定制化的任务流,即所谓的pipeline. 在传统的持续集成工具实现了pipeline功能,以供串联上下游job,并把多个job联系成一次完整的构建,例如jenkins的pipeline插件. 但是各种持续集成工具,或多或少都有自己的短板,总结起来如下: 1.配置并不

如何排查无线AP在使用过程中的故障?

无线网络技术的飞速发展致使无线AP已渐渐融入我们的生活并与之密不可分.无线AP主要运用于企业.商场超市.酒店餐厅.学校工厂.展览展会等场所,为其构建WLAN并提供WiFi覆盖.很多终端用户觉得使用无线AP构建的商用WiFi的用户体验并不好,实际上快速好用的商用WiFi不仅要有好的无线AP设备做硬件支持,商户也应该对环境特点.最大用户数.产品特性.安装布局等多方位因素有个整体的了解,否则很可能选不到合适的WiFi覆盖方案或者即使有了可行度比较高的WiFi覆盖方案,在使用过程中出现一点小问题就手足无

DHCP获取IP地址过程中捕获的报文—三级网络总结(二)

上一篇文章主要说了一下知识点中的IP地址的考点,这一篇我打算说说DHCP获取IP地址过程中捕获的报文的这个考点,都是自己的理解,有错误欢迎指正. DHCP是应用层协议,UDP是传输层协议,IP是网络层协议,以太网是链路层协议.数据在网络上传输的时候要自顶向下逐层封装的,典型的DHCP过程是这样的: 1:客户机向服务器发送DHCP_DISCOVER报文,申请IP. 2:服务器向客户机返会DHCP_OFFER报文,指定一个将要分配的IP. 3:客户机向服务器发送DHCP_REQUEST报文,请求这个

SSL协议的握手过程

SSL协议的握手过程SSL协议既用到了公钥加密技术(非对称加密)又用到了对称加密技术,SSL对传输内容的加密是采用的对称加密,然后对对称加密的密钥使用公钥进行非对称加密.SSL的握手协议非常有效的让客户和服务器之间完成相互之间的身份认证,其主要过程如下: ①客户端的浏览器向服务器传送客户端SSL协议的版本号,加密算法的种类,产生的随机数,以及其他服务器和客户端之间通讯所需要的各种信息. ②服务器向客户端传送SSL协议的版本号,加密算法的种类,随机数以及其他相关信息,同时服务器还将向客户端传送自己

Linux下安装MyEclipse和Tomcat服务器详解,以及我安装过程中所出现的问题以及解决办法,并实现一个web小程序

1.首先,先要去MyEclipse和Tomcat的官网去下载Linux版的压缩文件,而MyEclipse的中文官网是需要登录并有可能要付钱,大家可以去网上下载,还有就是Tomcat的linux版,这个直接上官网就可以下载了,下载后我还是通通把它们放在我E盘下的as目录底下,如果大家还没配置好jdk,即Java环境的话,可看我这篇文章:http://blog.csdn.net/u012561176/article/details/45119047 这里我就直接进行操作了! 我E盘下的as文件夹

APP store 上架过程中碰到的那些坑&amp;被拒的各种奇葩原因整理&amp;审核指南中文版

苹果官方发布的十大常见被拒原因 1.崩溃次数和Bug数量.苹果要求开发者在将应用提交给App Store之前彻查自己的应用,以尽量避免Bug的存在. 2.链或错误的链接.应用中所有的链接必须是真实且有效的. 3.占位符内容.有占位符内容的应用将无法审核通过. 4.提交的信息不完整.苹果要求开发者提供所有必须在iTunes Connect的应用审查信息区(App Review Information Section)中提交审查时所需要用到的所有完整信息.这是应用审核未通过最常见的原因,占到了14%

基于H.264协议的视频传输系统中遇到的问题以及解决办法

问题1.视频压缩解码模块在运用的时候出现错误:解码器再解码第二帧视频图片的时候出现异常 client: ../../decoder/T264dec.c:594:T264dec_decode_nal: Assertion `0' failed. Aborted 对于该问题的分析及解决过程为: 1.  修改数据类型,所有缓存区改为unsigned char类型(原来统一为char 类型),但是还是遇到一样的异常错误,问题没有解决. 2.  查看缓存区具体内容是否与服务器端压缩的数据是一致的的,对照数