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. enum EncodeState
  12. {
  13. KEY,
  14. VALUE,
  15. };
  16.  
  17. class BCode
  18. {
  19. private:
  20. static int index;
  21. static BaseData* RealDecodeToDic(string str, int& index, EncodeState state); public:
  22. static BaseData* DecodeToDic(string str); static string EncodeToStr(BaseData*);
  23. };
  1. #include "BCode.h"
  2. #include <sstream>
  3.  
  4. int BCode::index = ;
  5.  
  6. //refactoring! to support the users a simple interface, to delegate the real working function inner the interface!
  7. BaseData* BCode::DecodeToDic(string str)
  8. {
  9. return RealDecodeToDic(str, BCode::index, VALUE);
  10. }
  11.  
  12. //the recursion is for analyzing the dictionary!
  13. //it must can be used in other place!!
  14. BaseData* BCode::RealDecodeToDic(string str, int& index, EncodeState state)
  15. {
  16. DicData* dicData = new DicData();
  17. char c = str[index];
  18.  
  19. while( c != 'e')
  20. {
  21. if(c == 'd')
  22. {
  23. index ++;
  24. return RealDecodeToDic(str, index, KEY);
  25. }
  26. if(c == 'i')
  27. {
  28. string returnStr = "";
  29. index ++;
  30. //c = str[index];
  31. while(str[index] != 'e')
  32. {
  33. returnStr += str[index];
  34. index++;
  35. //c = str[index];
  36. }
  37. //transfer string to char, then transfer char to int
  38. IntData * intData = new IntData();
  39. //int returnInt = atoi(returnStr.c_str());
  40. intData->SetValue(atoi(returnStr.c_str()));
  41. return intData;
  42. }
  43. if(c == 'l')
  44. {
  45. index++;
  46. ListData* listData = new ListData();
  47. while (str[index] != 'e')
  48. {
  49. listData->add(RealDecodeToDic(str, index, VALUE));
  50. index++;
  51. }
  52. return listData;
  53. }
  54. if('' < c && c <= '')
  55. {
  56. string returnString = "";
  57. string contentString = "";
  58. while(str[index] != ':')
  59. {
  60. returnString += str[index];
  61. index++;
  62. }
  63. int stringLength = atoi(returnString.c_str());
  64. for (int i = ; i < stringLength; i++)
  65. {
  66. contentString += str[index + ];
  67. index++;
  68. }
  69.  
  70. if(state == VALUE)
  71. {
  72. StrData* strData = new StrData();
  73. strData->SetValue(contentString);
  74. return strData;
  75. }
  76. index++;
  77. dicData->add(contentString, RealDecodeToDic(str, index, VALUE));
  78. state = KEY;
  79. index++;
  80. }
  81. c = str[index];
  82. }
  83. return dicData;
  84. }
  85.  
  86. //a kind of recursion! it's smart!
  87. string BCode::EncodeToStr(BaseData* baseData)
  88. {
  89. string newString;
  90. if(baseData->GetDataType() == B_DIC)
  91. {
  92. DicData* dicData = static_cast<DicData*>(baseData);
  93. map<string, BaseData*> newMap = dicData->GetValue();
  94. newString.append("d");
  95.  
  96. for(map<string, BaseData*>::iterator it = newMap.begin(); it != newMap.end(); it++)
  97. {
  98. stringstream ss;
  99. ss << (it->first).length();
  100. newString = newString.append(ss.str() )+ ":" + it->first;
  101.  
  102. BaseData* recursionBaseData = it->second;
  103. newString.append(EncodeToStr(recursionBaseData));
  104. }
  105. newString.append("e");
  106. }
  107.  
  108. if(baseData->GetDataType() == B_INT)
  109. {
  110. IntData* intData = static_cast<IntData*>(baseData);
  111. //int iValue = intData->GetValue();
  112. stringstream ss;
  113. ss << (intData->GetValue());
  114. newString = "i" + newString.append(ss.str()) + "e";
  115. }
  116.  
  117. //can't reach here
  118. //use assert!
  119. if (baseData->GetDataType() == B_LIST)
  120. {
  121. newString.append("l");
  122. ListData* listData = static_cast<ListData*>(baseData);
  123. list<BaseData*> newList = listData->GetValue();
  124. for(list<BaseData*>::iterator it = newList.begin(); it != newList.end(); it++)
  125. {
  126. newString.append(EncodeToStr(*it));
  127. }
  128. newString.append("e");
  129. }
  130.  
  131. if (baseData->GetDataType() == B_STR)
  132. {
  133. StrData* strData = static_cast<StrData*>(baseData);
  134. stringstream ss;
  135. ss << (strData->GetValue()).length();
  136. //string newStr = strData->GetValue();
  137. newString = newString + ss.str() + ":" + (strData->GetValue());
  138. }
  139.  
  140. return newString;
  141. }

当然好的B编码必然对应好的数据结构,因为不管网络中传输的数据是什么样子的,

我们必须要在自己的程序内管理好数据结构才行,因为我们要在map中放入还不能

确定数据类型的数据,所以需要在map中放入父类实例的指针,以让我们能够在以后

还能通过指针使用多态的特性让子类去代替父类,看看代码理解下,当然这里使用了

几个面向对象语言的高级特性:设计模式中的composit模式,STL以及多态。大家

可以去了解下,当然这部分的代码绝大部分是我群里的“元古”大神所实现的数据结构。

代码和类图都在下面贴出来:

  1. #ifndef USEING
  2. #define USEING
  3.  
  4. #include <string>
  5. #include <list>
  6. #include <map>
  7. #include<assert.h>
  8. #include <iostream>
  9. #include <set>
  10. using namespace std;
  11.  
  12. typedef enum BeType
  13. {
  14. B_STR,
  15. B_INT,
  16. B_LIST,
  17. B_DIC,
  18. }BeType;
  19.  
  20. class BaseData
  21. {
  22. public:
  23. BeType beType;
  24.  
  25. public:
  26. //stick to polymorphisms
  27. //virtual ~BaseData();
  28. virtual BeType GetDataType()
  29. {
  30. return beType;
  31. };
  32. virtual void SetDataType(BeType beType){};
  33.  
  34. };
  35.  
  36. class StrData : public BaseData
  37. {
  38. private:
  39. string sValue;
  40. public:
  41. StrData():sValue("")
  42. {
  43. SetDataType(B_STR);
  44. };
  45. BeType GetDataType()
  46. {
  47. return beType;
  48. }
  49. void SetDataType(BeType newBeType)
  50. {
  51. beType = newBeType;
  52. }
  53.  
  54. string GetValue() {return sValue; };
  55. void SetValue(char byte) {sValue.append(&byte, ); };
  56. void SetValue(char* bytes)
  57. {
  58. int length = sizeof(bytes);
  59. sValue.append(bytes, length);
  60. };
  61. void SetValue(string str)
  62. {
  63. sValue.append(str);
  64. };
  65. };
  66.  
  67. class IntData : public BaseData
  68. {
  69. private:
  70. int iValue;
  71. public:
  72. IntData():iValue()
  73. {
  74. SetDataType(B_INT);
  75. }
  76. BeType GetDataType()
  77. {
  78. return beType;
  79. }
  80. void SetDataType(BeType newBeType)
  81. {
  82. beType = newBeType;
  83. }
  84.  
  85. int GetValue()
  86. {
  87. return iValue;
  88. }
  89. void SetValue(char* numStr)
  90. {
  91. iValue = atoi(numStr);
  92. }
  93. void SetValue(int num)
  94. {
  95. iValue = num;
  96. }
  97. };
  98.  
  99. class ListData : public BaseData
  100. {
  101. private:
  102. //there is a strong relationship between ListData and BaseData, it is named "compose" , it is also a design pattern!
  103. list<BaseData*> lValue;
  104. public:
  105. ListData():lValue()
  106. {
  107. SetDataType(B_LIST);
  108. };
  109. BeType GetDataType()
  110. {
  111. return beType;
  112. }
  113. void SetDataType(BeType newBeType)
  114. {
  115. beType = newBeType;
  116. }
  117.  
  118. list<BaseData*> GetValue()
  119. {
  120. return lValue;
  121. }
  122. void add(BaseData* baseData)
  123. {
  124. lValue.push_back(baseData);
  125. }
  126. //destructor! it will be called when user use the "delete" key, and inner ListData , BaseData will also call delete!
  127. ~ListData()
  128. {
  129. for(list<BaseData*>::iterator it = lValue.begin(); it != lValue.end(); it++)
  130. {
  131. if(*it != NULL)
  132. delete *it;
  133. }
  134. lValue.clear();
  135. }
  136. };
  137.  
  138. class DicData : public BaseData
  139. {
  140. private:
  141. //the reason to use the pointer,because we need to transfer the father to the son, it is polymorphisms
  142. //remember
  143. map<string, BaseData*> dValue;
  144.  
  145. public:
  146. //no initialize for map
  147. DicData()
  148. {
  149. SetDataType(B_DIC);
  150. };
  151. BeType GetDataType()
  152. {
  153. return beType;
  154. }
  155. void SetDataType(BeType newBeType)
  156. {
  157. beType = newBeType;
  158. }
  159.  
  160. map<string, BaseData*> GetValue()
  161. {
  162. return dValue;
  163. }
  164. //except the second value, we can also insert some else value like string to StrData, int to IntData, list to ListData!
  165. void add(string str, BaseData* baseData)
  166. {
  167. dValue.insert(make_pair(str, baseData));
  168. }
  169. void add(char* bytes, BaseData* baseData)
  170. {
  171. dValue.insert(make_pair(bytes, baseData));
  172. }
  173. void add(string strOne, string strTwo)
  174. {
  175. StrData* strData = new StrData();
  176. strData->SetValue(strTwo);
  177. dValue.insert(make_pair(strOne, strData));
  178. }
  179. void add(string str, char* bytes, int lengh)
  180. {
  181. string tempStr = "";
  182. tempStr.append(bytes, lengh);
  183. add(str, tempStr);
  184. }
  185. ~DicData()
  186. {
  187. for(map<string, BaseData*>::iterator it = dValue.begin(); it != dValue.end(); it++)
  188. {
  189. //attention the different between the map and list
  190. //the iterator is a pointer, but we've used the ->
  191. if(it->second != NULL)
  192. delete it->second;
  193. }
  194. dValue.clear();
  195. }
  196. };
  197. #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. Android APP 简单高效的禁用横竖屏切换

    默认情况下,Android APP的界面会随着手机方向的改变而改变,当手机处于竖屏状态,APP的界面也处于竖屏状态,而当手机处于横屏状态,APP也会自动切换到横屏状态.一般情况下APP的界面都是为竖屏 ...

  2. web前端(实习生)之“百度二面”

    2016.3.18,星期五.我经历了我的第一次面试. 在面完一面之后,面试官说“我对你的考核到这里结束了,我去看一下公司是决定现在就安排二面还是只有再做安排,你先在这里等一下.”我当时就蒙圈了:一是在 ...

  3. 【python之路1】python安装与环境变量配置

    直接搜索 Python,进入官网,找到下载,根据个人电脑操作系统下载相应的软件.小编的是windows os .下载python-2.7.9.msi 安装包  双击安装程序,进入安装步骤.在安装过程中 ...

  4. idea 插件的使用 进阶篇

    CSDN 2016博客之星评选结果公布    [系列直播]零基础学习微信小程序!      "我的2016"主题征文活动   博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...

  5. ASP.NET之纠错

    甘特图是:计划项目的时间安排和资源平衡 IoC容器会自动判断javabean和某些数据类型是否建立起依赖关系(装配是否成功),主要用在自动装配中. 通过设置属性<bean id="&q ...

  6. Nhibernate mapping 文件编写

    生成工具软件 现在生成工具软件有很多了,例如商业软件:NMG.CodeSmith.Visual NHibernate,开源软件:MyGeneration.NHibernate Modeller.AjG ...

  7. SqlServer按中文数字排序

    表数据: 按名称排序 并不能得到一二三四五六的顺序 select * from LiWei order by name 找到中文数字在'一二三四五六七八九十'的位置 select id,name,SU ...

  8. docker 启动安装等命令

    确认是否安装url whereis curl 启动docker服务: sudo service docker start sudo service docker stop 安装curl sudo ap ...

  9. java的反射

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  10. ROS中DDNS的使用

    一.通过tool fetch更新ddns,关于此命令的使用,参考 tool fetch Scripts中添加脚本/tool fetch url="http://www.51kwl.com/? ...