首页 > 代码库 > DHT协议C++实现过程中各种问题

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 EncodeState13 {14     KEY,15     VALUE,16 };17 18 class BCode19 {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 = http://www.mamicode.com/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 = http://www.mamicode.com/new IntData();            //int returnInt = atoi(returnStr.c_str());            intData->SetValue(atoi(returnStr.c_str()));            return intData;        }        if(c == l)        {            index++;            ListData* listData = http://www.mamicode.com/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 = http://www.mamicode.com/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 = http://www.mamicode.com/static_cast(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 = http://www.mamicode.com/it->second;            newString.append(EncodeToStr(recursionBaseData));        }        newString.append("e");    }    if(baseData->GetDataType() == B_INT)    {        IntData* intData = http://www.mamicode.com/static_cast(baseData);        //int iValue = http://www.mamicode.com/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 = http://www.mamicode.com/static_cast(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 = http://www.mamicode.com/static_cast(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 = http://www.mamicode.com/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*,可惜了如此漂亮的递归啊!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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