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编码程序,可以跑起来,但是有一个致命的问题存在,容后再说,先贴

代码:

 #define USING
#include "DataStruction.h"
#include <string>
using namespace std; /*
https://github.com/CreateChen/Bencode
transfer the CreateChen's c# code into c++, the thought is very impressive!
*/ enum EncodeState
{
KEY,
VALUE,
}; class BCode
{
private:
static int index;
static BaseData* RealDecodeToDic(string str, int& index, EncodeState state); public:
static BaseData* DecodeToDic(string str); static string EncodeToStr(BaseData*);
};
#include "BCode.h"
#include <sstream> int BCode::index = ; //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('' < c && c <= '')
{
string returnString = "";
string contentString = "";
while(str[index] != ':')
{
returnString += str[index];
index++;
}
int stringLength = atoi(returnString.c_str());
for (int i = ; i < stringLength; i++)
{
contentString += str[index + ];
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, ); };
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()
{
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()
{
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*,可惜了如此漂亮的递归啊!

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

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

  1. HTTPS 中双向认证SSL 协议的具体过程

    HTTPS 中双向认证SSL 协议的具体过程: 这里总结为详细的步骤: ① 浏览器发送一个连接请求给安全服务器.② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器.③ 客户浏览器检查服务器送 ...

  2. BitTorrent DHT 协议中文翻译

    前言 做了一个磁力链接和BT种子的搜索引擎 {Magnet & Torrent},因此把 DHT 协议重新看了一遍. BitTorrent 使用"分布式哈希表"(DHT)来 ...

  3. HTTP协议请求响应过程和HTTPS工作原理

    HTTP协议 HTTP协议主要应用是在服务器和客户端之间,客户端接受超文本. 服务器按照一定规则,发送到客户端(一般是浏览器)的传送通信协议.与之类似的还有文件传送协议(file transfer p ...

  4. lua解析脚本过程中的关键数据结构介绍

    在这一篇文章中我先来介绍一下lua解析一个脚本文件时要用到的一些关键的数据结构,为将来的一系列代码分析打下一个良好的基础.在整个过程中,比较重要的几个源码文件分别是:llex.h,lparse.h.l ...

  5. 解决 java 使用ssl过程中出现"PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target"

    今天,封装HttpClient使用ssl时报一下错误: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorExc ...

  6. 从输入 URL 到浏览器接收的过程中发生了什么事情

    从输入 URL 到浏览器接收的过程中发生了什么事情? 原文:http://www.codeceo.com/article/url-cpu-broswer.html 从触屏到 CPU  首先是「输入 U ...

  7. LTE 切换过程中的数据切换

    http://blog.sina.com.cn/s/blog_673b30dd0100j4p4.html LTE中的切换,根据无线承载(Radio Bearer)的QoS要求的不同,可以分为无缝切换( ...

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

    苹果官方发布的十大常见被拒原因 1.崩溃次数和Bug数量.苹果要求开发者在将应用提交给App Store之前彻查自己的应用,以尽量避免Bug的存在. 2.链或错误的链接.应用中所有的链接必须是真实且有 ...

  9. 关于mapreduce过程中出现的错误:Too many fetch-failures

    Reduce task启动后第一个阶段是shuffle,即向map端fetch数据.每次fetch都可能因为connect超时,read超时,checksum错误等原因而失败.Reduce task为 ...

随机推荐

  1. 【转】XenServer体系架构解析

    XenServer是一套已在云计算环境中经过验证的企业级开放式服务器虚拟化解决方案,可以将静态.复杂的IT环境转变为更加动态.易于管理的虚拟数据中心,从而大大降低数据中心成本.同时,它可以提供先进的管 ...

  2. UploadHandleServlet

    public class UploadHandleServlet extends HttpServlet { public void doGet(HttpServletRequest request, ...

  3. [LeetCode] Longest Increasing Path in a Matrix 矩阵中的最长递增路径

    Given an integer matrix, find the length of the longest increasing path. From each cell, you can eit ...

  4. 如何为Surface Dial设备开发自定义交互功能

    随着Surface Studio的发布,微软发布了与之相配套的外设硬件Surface Dial,用户可以将Surface Dail吸附在Surface Studio的屏幕上面,用旋转和点击的实体操作来 ...

  5. REDIS持久化报错失败

    redis log报错: [7666] 15 Jan 00:22:36.028 # Error moving temp DB file on the final destination: Invali ...

  6. Python学习--Python 环境搭建

    Python环境搭建 Python是跨平台的编程语言,可应用于Windows.Linux.Mac OS X.你可以通过终端窗口输入"python"命令来查看本地是否安装了Pytho ...

  7. weui dialog

    切记:weui dialog 的样式是在weui.css,而不是在weui.min.css HTML: <!DOCTYPE html> <html> <head> ...

  8. System.Environment.CurrentDirectory和Application.StartupPath

    System.Environment.CurrentDirectory的含义是获取或设置当前工作路径,而Application.StartupPath是获取程序启动路径,表面上看二者没什么区别,但实际 ...

  9. 关于CAJViewer 无法获取document路径问题

    修改注册表 进入注册表编辑器: win+R >>输入 regedit  如下图: 修改关键注册表项(两项相同值应同时修改) 1.HKEY_CURRENT_USER\Software\Mic ...

  10. pecl 轻松安装php扩展

    PECL 的全称是 The PHP Extension Community Library ,是一个开放的并通过 PEAR(PHP Extension and Application Reposito ...