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为 ...
随机推荐
- Android APP 简单高效的禁用横竖屏切换
默认情况下,Android APP的界面会随着手机方向的改变而改变,当手机处于竖屏状态,APP的界面也处于竖屏状态,而当手机处于横屏状态,APP也会自动切换到横屏状态.一般情况下APP的界面都是为竖屏 ...
- web前端(实习生)之“百度二面”
2016.3.18,星期五.我经历了我的第一次面试. 在面完一面之后,面试官说“我对你的考核到这里结束了,我去看一下公司是决定现在就安排二面还是只有再做安排,你先在这里等一下.”我当时就蒙圈了:一是在 ...
- 【python之路1】python安装与环境变量配置
直接搜索 Python,进入官网,找到下载,根据个人电脑操作系统下载相应的软件.小编的是windows os .下载python-2.7.9.msi 安装包 双击安装程序,进入安装步骤.在安装过程中 ...
- idea 插件的使用 进阶篇
CSDN 2016博客之星评选结果公布 [系列直播]零基础学习微信小程序! "我的2016"主题征文活动 博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...
- ASP.NET之纠错
甘特图是:计划项目的时间安排和资源平衡 IoC容器会自动判断javabean和某些数据类型是否建立起依赖关系(装配是否成功),主要用在自动装配中. 通过设置属性<bean id="&q ...
- Nhibernate mapping 文件编写
生成工具软件 现在生成工具软件有很多了,例如商业软件:NMG.CodeSmith.Visual NHibernate,开源软件:MyGeneration.NHibernate Modeller.AjG ...
- SqlServer按中文数字排序
表数据: 按名称排序 并不能得到一二三四五六的顺序 select * from LiWei order by name 找到中文数字在'一二三四五六七八九十'的位置 select id,name,SU ...
- docker 启动安装等命令
确认是否安装url whereis curl 启动docker服务: sudo service docker start sudo service docker stop 安装curl sudo ap ...
- java的反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- ROS中DDNS的使用
一.通过tool fetch更新ddns,关于此命令的使用,参考 tool fetch Scripts中添加脚本/tool fetch url="http://www.51kwl.com/? ...