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编码程序,可以跑起来,但是有一个致命的问题存在,容后再说,先贴
代码:
#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++实现过程中各种问题的更多相关文章
- HTTPS 中双向认证SSL 协议的具体过程
		
HTTPS 中双向认证SSL 协议的具体过程: 这里总结为详细的步骤: ① 浏览器发送一个连接请求给安全服务器.② 服务器将自己的证书,以及同证书相关的信息发送给客户浏览器.③ 客户浏览器检查服务器送 ...
 - BitTorrent DHT 协议中文翻译
		
前言 做了一个磁力链接和BT种子的搜索引擎 {Magnet & Torrent},因此把 DHT 协议重新看了一遍. BitTorrent 使用"分布式哈希表"(DHT)来 ...
 - HTTP协议请求响应过程和HTTPS工作原理
		
HTTP协议 HTTP协议主要应用是在服务器和客户端之间,客户端接受超文本. 服务器按照一定规则,发送到客户端(一般是浏览器)的传送通信协议.与之类似的还有文件传送协议(file transfer p ...
 - lua解析脚本过程中的关键数据结构介绍
		
在这一篇文章中我先来介绍一下lua解析一个脚本文件时要用到的一些关键的数据结构,为将来的一系列代码分析打下一个良好的基础.在整个过程中,比较重要的几个源码文件分别是:llex.h,lparse.h.l ...
 - 解决 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 ...
 - 从输入 URL 到浏览器接收的过程中发生了什么事情
		
从输入 URL 到浏览器接收的过程中发生了什么事情? 原文:http://www.codeceo.com/article/url-cpu-broswer.html 从触屏到 CPU 首先是「输入 U ...
 - LTE 切换过程中的数据切换
		
http://blog.sina.com.cn/s/blog_673b30dd0100j4p4.html LTE中的切换,根据无线承载(Radio Bearer)的QoS要求的不同,可以分为无缝切换( ...
 - APP store 上架过程中碰到的那些坑&被拒的各种奇葩原因整理&审核指南中文版
		
苹果官方发布的十大常见被拒原因 1.崩溃次数和Bug数量.苹果要求开发者在将应用提交给App Store之前彻查自己的应用,以尽量避免Bug的存在. 2.链或错误的链接.应用中所有的链接必须是真实且有 ...
 - 关于mapreduce过程中出现的错误:Too many fetch-failures
		
Reduce task启动后第一个阶段是shuffle,即向map端fetch数据.每次fetch都可能因为connect超时,read超时,checksum错误等原因而失败.Reduce task为 ...
 
随机推荐
- Nginx+PHP On windows
			
前期准备 Nginx 下载 http://nginx.org/ PHP下载 PHP Windows binaries 问题 问题1: opened a DOS window to la ...
 - nth-of-type在选择class的时候需要注意的一个小问题
			
查了下w3和MDN的手册,没发现有这个说明,写篇随笔记下. 1..class:nth-of-type(n)在选择class的时候,如果在class前面插入x个同类型标签,n需要加上x <!DOC ...
 - pmd静态代码分析
			
在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...
 - 理解HTML语义化
			
1.什么是HTML语义化? <基本上都是围绕着几个主要的标签,像标题(H1~H6).列表(li).强调(strong em)等等> 根据内容的结构化(内容语义化),选择合适的标签(代码语义 ...
 - Entity Framework关于SQL注入安全问题
			
1.EF生成的sql语句,用 parameter 进行传值,所以不会有sql注入问题 2.EF下有涉及外部输入参数传值的,禁止使用EF直接执行sql命令方式,使用实体 SQL 参考: https: ...
 - 腾讯GT的流畅度测试方案研究
			
GT源码:https://github.com/TencentOpen/GT 一.流畅度模块的代码结构 流畅度插件总共就几个类,其实处理方式也比较简单粗暴,就是通过Choreographer输出的lo ...
 - 【WPF】新复制wpf项目报错
			
错误提示:Program does not contain a static 'Main' method suitable for an entry point 1.App.xaml 文件属性:生成操 ...
 - 详解mysql如何配置远程链接,解决各种连接问题
			
在服务器上面我们经常需要去使用mysql,有些童鞋刚刚配置好了服务器,想在本地的一些图形化软件去连接mysql得到更直观的表格显示,此时很可能不允许连接,为了探究为什么连接失败,在这里我会对mysql ...
 - 在移动端中的flex布局
			
flex布局介绍: flex布局很灵活, 这种布局我们也可以称之为弹性布局, 弹性布局的主要优势就是元素的宽或者高会自动补全; flex布局实例: 比如有两个div,一个div的宽度为100px, ...
 - EF 二级缓存 EFSecondLevelCache
			
EFSecondLevelCache ======= Entity Framework .x Second Level Caching Library. 二级缓存是一个查询缓存.EF命令的结果将存储在 ...