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. [总结] Versions crashing in OS X Yosemite (10.10)

    在文本编辑器中打开 ~/.subversion/servers 在 [global] 下添加该行: http-library = serf 然后,安装 Versions 1.3.2

  2. js控制div滚动条,滚动滚动条使div中的元素可见并居中

    1.html代码如下 <div id="panel"> <div id="div1"></div> <div id=& ...

  3. [转]Code! MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#)

    本文转自:https://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-o ...

  4. 基于pcDuino-V2的无线视频智能小车 - UBUNTU系统上的gtk编程

    详细的代码已经上传到git网站:https://github.com/qq2216691777/pcduino_smartcar

  5. 代码管理工具 --- git的学习笔记二《git的工作原理》

    通过几个问题来学习代码管理工具之git 一.git是什么?为什么要用它?使用它的好处?它与svn的区别,在Mac上,比较好用的git图形界面客户端有 git 是分布式的代码管理工具,使用它是因为,它便 ...

  6. 完整部署CentOS7.2+OpenStack+kvm 云平台环境(5)--问题解决

    一.[root@openstack-server ~]# nova listERROR (CommandError): You must provide a username or user id v ...

  7. [LeetCode] UTF-8 Validation 编码验证

    A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules: For 1-byte char ...

  8. 如何装最多的水? — leetcode 11. Container With Most Water

    炎炎夏日,还是呆在空调房里切切题吧. Container With Most Water,题意其实有点噱头,简化下就是,给一个数组,恩,就叫 height 吧,从中任选两项 i 和 j(i <= ...

  9. Spring和Mybatis整合,配置文件

    整合时除了未整合前spring.mybatis的必须包外还需要加入两个包 spring-jdbc-4.2.5.RELEASE.jar mybatis-spring-1.2.5.jar spring-j ...

  10. python基础补漏-05-生成器和装饰器

    [1]生成器 很难用简单的语言描述生成器. 生成器:从字面上来理解,就是以某种规则为基础,不断的生成数据的工具 生成器函数: 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器 ...