zoukankan      html  css  js  c++  java
  • 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数据,还有''!!

    于是cout出来呈现这样:

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

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

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

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

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

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

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

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

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

  • 相关阅读:
    深入A标签点击触发事件而不跳转的详解
    js、css、html判断浏览器的各种版本
    深入理解this对象
    背景透明文字不透明的最佳方法兼容IE(以背景黑色透明度0.5为例)
    解决ie6支持最大高度最小高度的方法
    js点击更多显示更多内容效果
    artdialog关闭弹出窗口
    (巧用)事件代理
    CSS3盒模型display:-webkit-box;的使用
    文件上传input type="file"样式美化
  • 原文地址:https://www.cnblogs.com/beneathginkgo/p/4096062.html
Copyright © 2011-2022 走看看