# 中软面试题-最新 ----- 以及 p2p协议分析

28 篇文章 0 订阅

by jiang zhuo on 七.29, 2010, under 笔试面试题目

中软的面试比较经典，也比较严格，一般有四轮，类似于微软的面试。中软面过以后，根据项目组，会推到美国微软那边运用live meeting & con-call 再面一次。以下是我的面试题及个人的小分析，拿出来和大家share一下。希望更多的人能过这个坎。如有什么问题，可以一起交流。直接进入主题：

1. English communication. (sale yourself, project information, your interesting,and how to deal with problem    you encounter etc.)

2.  the using of key words "new".

Regarding this problem, you can refer to my other blog with this path: new . or you can get more information from internet.

3.Write a method which can remove the same unit from a Array which has been sorted.

//在排序好的数组中移除相同的元素

public int [] RemoveCommon(int [] a)
{
if (a.Length==0)
{
return a;
}
List <int> Result = new List<int>();
int i , j;
i = j = 0;
int temp = a[0];
while (i < a.Length)
{
if (a[i] != temp)
{
j++;
temp = a[i];
}
i++;
}
// convert List to Array
//……
return Result;
}

4. Judge Triangle and write test case for your code.

public int Triangle(int a, int b, int c)
{
if (a <= 0 || b <= 0 || c <= 0)
{
return 4;
}
int [] arry = new int [3] { a, b, c };
Array.Sort(arry);
int min, mid, max;
min = arry[0];
mid = arry[1];
max = arry[2];
if (max-mid<min)  // 注意:用这个去判断是否能构成三角形
{
return 4;  //不能构成三角形
}
if (min == max)
{
return 1;  //等边
}
else if ( mid==min || mid == max)
{
return 2; // 等腰
}
else
return 3;   // 其他
}

Test case：

5.Reverse string.

private char [] Convent(char [] str,int start, int end)
{
char temp;
int len = end – start;
int i=0;
while(i<len/2)
{
temp = str[start+i];
str[start +i] = str[end -i - 1];
str[end-i-1] = temp;
i++;
}
return str;
}
public string Reverse(string str)
{
if (String.IsNullOrEmpty(str))
{
return null;
}
char [] objstr = str.ToCharArray(); ;
int length=objstr.Length;
objstr = Convent(objstr,0,length);
int i = 0;
int start=0,end=0;
while (i < length)
{
if (objstr[i] == ‘ ‘||i==length-1)
{
if (i == length – 1)
{
end = i + 1;
}
else
{
end = i;
}
objstr = Convent(objstr, start, end);
start = end+1;
}
i++;
}
return new string(objstr);
}

6. Find the most width level in a tree and return the count of level, if there are many one, just return the nearest level. (it can be found in the internet)

static int  M 10 //假设二叉树最多的层数
int Width(BinTree T)

int static n[M];//向量存放各层结点数
int static i=1;
int static max=0;//最大宽度
if(T)
{
if(i==1) //若是访问根结点
{
n[i]++; //第1层加1
i++; //到第2层
if(T->lchild)//若有左孩子则该层加1
n[i]++;
if(T->rchild)//若有右孩子则该层加1
n[i]++;
}
else
{ //访问子树结点
i++; //下一层结点数
if(T->lchild)
n[i]++;
if(T->rchild)
n[i]++;
}
if(max<n[i])max=n[i];//取出最大值
Width(T->lchild);//遍历左子树
i–; //往上退一层
Width(T->rchild);//遍历右子树
}
return max;
}//算法结束

7. Implement the function: Int ConvertToInt(string num)

public int ConvertToInt(string num)
{
int result=0;
int temp=0;
if (!string.IsNullOrEmpty(num))
{
if (IsInteger(num))
{
for (int i = 0; i < num.Length; i++)
{
temp = result;
result = result * 10 + ((int)num[i] – 48); //0 的Asscall码 是48
if (temp == result)
continue;
}
if (temp != result)
{
throw new Exception("overflow");
}
}
}
return result;
}
// 判断字符串是否是整数。
public bool IsInteger(string strIn)
{
bool bolResult = true;
if (strIn == "")
{
bolResult = false;
}
else
{
foreach (char Char in strIn)
{
if (char.IsNumber(Char))
continue;
else
{
bolResult = false;
break;
}
}
}
return bolResult;
}

关于上面的判断字符串里转换后是否是整数，还有其他的方法：

public bool isnumeric(string str)
{
char[] ch = new char[str.Length];
ch = str.ToCharArray();
for (int i = 0; i < ch.Length; i++)
{
if (ch[i] < 48 || ch[i] > 57)
return false;
}
return true;
}

8. Quick sort. (you can get it from internet)

static public void Quicksort(int[] array, int begin, int end)
{
if (begin < 0 || end < 0 || begin > end)
return;
int left = begin, right = end, temp;
temp = array[left];
while (right != left)
{
while (temp < array[right] && right > left)
right–;
if (right > left)
{
array[left] = array[right];
left++;
}
while (temp > array[left] && right > left)
left++;
if (right > left)
{
array[right] = array[left];
right–;
}
}
array[right] = temp;
Quicksort(array, right + 1, end);
Quicksort(array, begin, right – 1);
}

Ok, that is all.

### 从Announce参数与Scrape参数看Announce与Scrape的不同

by jiang zhuo on 七.29, 2010, under 源码分析

Announce的参数比较多 常用于发布种子向Tracker提交信息 并且一次是一个种子。

Scrape的参数比较少 从Tracker一次获得多个Torrent的信息。

Announce的参数处理
{
CheckMandatoryFields();
if (!isValid)
return;

/* If the user has supplied an IP address, we use that instead of
* the IP address we read from the announce request connection. */
else
}

private void CheckMandatoryFields()
{
isValid = false;

List<string> keys = new List<string>(Parameters.AllKeys);
foreach (string field in mandatoryFields)
{
if (keys.Contains(field))
continue;

Response.Add(FailureKey, (BEncodedString)("mandatory announce parameter " + field + " in query missing"));
return;
}
byte[] hash = HttpUtility.UrlDecodeToBytes(Parameters["info_hash"]);
if (hash.Length != 20)
{
Response.Add(FailureKey, (BEncodedString)(string.Format("infohash was {0} bytes long, it must be 20 bytes long.", hash.Length)));
return;
}
infoHash = new InfoHash(hash);
isValid = true;
}


Scrape的参数处理        public ScrapeParameters(NameValueCollection collection, IPAddress address)
{
hashs = new List<InfoHash>();
ParseHashes(Parameters["info_hash"]);
}

private void ParseHashes(string infoHash)
{
if (string.IsNullOrEmpty(infoHash))
return;

if (infoHash.IndexOf(',') > 0)
{
string[] stringHashs = infoHash.Split(',');
for (int i = 0; i < stringHashs.Length; i++)
}
else
{
}
}


### BT网络服务器-Listener及其相关类的实现与源码分析-UdpListener

by jiang zhuo on 七.27, 2010, under 源码分析

connectionIDs是个Dictionary 用来维护现在Listener处理的所有传入连接

curConnectionID是当前处理的传入连接

Running标识是否正在运行

CreatConnectionID没创建一个curConnectionID进行加一，加入到Dictionary 中是传入来连接的IP与curConnectionID的集合

GetConnectionID根据IP得到connectionID

Start与Stop方法
/// <summary>
/// Starts listening for incoming connections
/// </summary>
public override void Start()
{
if (Running)
return;

//TODO test if it is better to use socket directly
//Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

listener = new System.Net.Sockets.UdpClient(endpoint.Port);
}

/// <summary>
/// Stops listening for incoming connections
/// </summary>
public override void Stop()
{
if (!Running)
return;
System.Net.Sockets.UdpClient listener = this.listener;
this.listener = null;
listener.Close();
}


Stop方法释放UdpClient

ReceiveData方法
{
try
{
byte[] data = listener.EndReceive(ar, ref endpoint);
if (data.Length <16)

UdpTrackerMessage request = UdpTrackerMessage.DecodeMessage(data, 0, data.Length, MessageType.Request);

switch (request.Action)
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
default:
throw new ProtocolException(string.Format("Invalid udp message received: {0}", request.Action));
}
}
catch (Exception e)
{
Logger.Log(null, e.ToString());
}
finally
{
if (Running)
}
}


Receive到Connection Announce Scrape的方法        private void ReceiveConnect(ConnectMessage connectMessage)
{
UdpTrackerMessage m = new ConnectResponseMessage(connectMessage.TransactionId, CreateConnectionID());
byte[] data = m.Encode();
listener.Send(data, data.Length, endpoint);
}

//QUICKHACK: format bencoded val and get it back wereas must refactor tracker system to have more generic object...
{
UdpTrackerMessage m;
BEncodedDictionary dict = Handle(getCollection(announceMessage), endpoint.Address, false);
if (dict.ContainsKey(RequestParameters.FailureKey))
{
m = new ErrorMessage(announceMessage.TransactionId, dict[RequestParameters.FailureKey].ToString());
}
else
{
TimeSpan interval = TimeSpan.Zero;
int leechers = 0;
int seeders = 0;
List<MonoTorrent.Client.Peer> peers = new List<MonoTorrent.Client.Peer>();
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in dict)
{
switch (keypair.Key.Text)
{
case ("complete"):
seeders = Convert.ToInt32(keypair.Value.ToString());//same as seeder?
break;

case ("incomplete"):
leechers = Convert.ToInt32(keypair.Value.ToString());//same as leecher?
break;

case ("interval"):
interval = TimeSpan.FromSeconds(int.Parse(keypair.Value.ToString()));
break;

case ("peers"):
if (keypair.Value is BEncodedList)          // Non-compact response
else if (keypair.Value is BEncodedString)   // Compact response
break;

default:
break;
}
}
m = new AnnounceResponseMessage(announceMessage.TransactionId, interval, leechers, seeders, peers);
}
byte[] data = m.Encode();
listener.Send(data, data.Length, endpoint);
}

private NameValueCollection getCollection(AnnounceMessage announceMessage)
{
NameValueCollection res = new NameValueCollection();
return res;
}

{
BEncodedDictionary val = Handle(getCollection(scrapeMessage), endpoint.Address, true);

UdpTrackerMessage m;
byte[] data;
if (val.ContainsKey(RequestParameters.FailureKey))
{
m = new ErrorMessage(scrapeMessage.TransactionId, val[RequestParameters.FailureKey].ToString());
}
else
{
List<ScrapeDetails> scrapes = new List<ScrapeDetails>();
BEncodedDictionary files = (BEncodedDictionary)val["files"];

foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair in val)
{
BEncodedDictionary dict = (BEncodedDictionary)keypair.Value;
int seeds = 0;
int leeches = 0;
int complete = 0;
foreach (KeyValuePair<BEncodedString, BEncodedValue> keypair2 in dict)
{
switch (keypair2.Key.Text)
{
case "complete"://The current number of connected seeds
seeds = Convert.ToInt32(keypair2.Value.ToString());
break;
complete = Convert.ToInt32(keypair2.Value.ToString());
break;
case "incomplete":
leeches = Convert.ToInt32(keypair2.Value.ToString());
break;
}
}
ScrapeDetails sd = new ScrapeDetails(seeds, leeches, complete);
if (scrapes.Count == 74)//protocole do not support to send more than 74 scrape at once...
{
m = new ScrapeResponseMessage(scrapeMessage.TransactionId, scrapes);
data = m.Encode();
listener.Send(data, data.Length, endpoint);
scrapes.Clear();
}
}
m = new ScrapeResponseMessage(scrapeMessage.TransactionId, scrapes);
}
data = m.Encode();
listener.Send(data, data.Length, endpoint);
}

private NameValueCollection getCollection(ScrapeMessage scrapeMessage)
{
NameValueCollection res = new NameValueCollection();
if (scrapeMessage.InfoHashes.Count == 0)
return res;//no infohash????
//TODO more than one infohash : paid attention to order in response!!!
InfoHash hash = new InfoHash(scrapeMessage.InfoHashes[0]);
return res;
}

{
}


### BT网络服务器-Listener及其相关类的实现与源码分析-HttpListener

by jiang zhuo on 七.27, 2010, under 源码分析

#### ListenerBase

ListenerBase是所有Listener的基类，平时使用的大多是HttpListener，我们见到的大多数Tracker发布都是通过Http协议的POST、GET实现的。

ParseQuery是URL中POST来的各个参数进行分析，得到一个名字与值的集合，选用System.Collections.Specialized中的NameValueCollection实在是再合适不过了。

Start与Stop方法是虚方法，在其派生类中有不同的实现。

Methods        #region Methods

{
if (queryString == null)
throw new ArgumentNullException("queryString");

}

{
if (collection == null)
throw new ArgumentNullException("collection");

RequestParameters parameters;
if (isScrape)
else

// If the parameters are invalid, the failure reason will be added to the response dictionary
if (!parameters.IsValid)
return parameters.Response;

// Fire the necessary event so the request will be handled and response filled in
if (isScrape)
else

// Return the response now that the connection has been handled correctly.
return parameters.Response;
}

private NameValueCollection ParseQuery(string url)
{
// The '?' symbol will be there if we received the entire URL as opposed to
// just the query string - we accept both therfore trim out the excess if we have the entire URL
if (url.IndexOf('?') != -1)
url = url.Substring(url.IndexOf('?') + 1);

string[] parts = url.Split('&', '=');
NameValueCollection c = new NameValueCollection(1 + parts.Length / 2);
for (int i = 0; i < parts.Length; i += 2)
if (parts.Length > i + 1)

return c;
}

{
if (h != null)
h(this, e);
}

{
if (h != null)
h(this, e);
}

public abstract void Start();

public abstract void Stop();

#endregion Methods


#### HttpListener

HttpListener
using System;
using System.Net;
using MonoTorrent.BEncoding;

namespace MonoTorrent.Tracker.Listeners
{
public class HttpListener : ListenerBase
{
#region Fields

private string prefix;
private System.Net.HttpListener listener;

#endregion Fields

#region Properties

/// <summary>
/// 如果Listener正在监听则为True
/// </summary>
public override bool Running
{
get { return listener != null; }
}

#endregion Properties

#region Constructors

{

}

public HttpListener(IPEndPoint endpoint)
{

}

public HttpListener(string httpPrefix)
{
if (string.IsNullOrEmpty(httpPrefix))
throw new ArgumentNullException("httpPrefix");

this.prefix = httpPrefix;
}

#endregion Constructors

#region Methods

/// <summary>
/// 开始监听
/// </summary>
public override void Start()
{
if (Running)
return;

listener = new System.Net.HttpListener();
listener.Start();
listener.BeginGetContext(EndGetRequest, listener);
}

/// <summary>
/// 停止监听
/// </summary>
public override void Stop()
{
if (!Running)
return;

listener.Close();
listener = null;
}

private void EndGetRequest(IAsyncResult result)
{
HttpListenerContext context = null;
System.Net.HttpListener listener = (System.Net.HttpListener) result.AsyncState;

try
{
context = listener.EndGetContext(result);
HandleRequest(context);
}
catch(Exception ex)
{
Console.Write("Exception in listener: {0}{1}", Environment.NewLine, ex);
}
finally
{
if (context != null)
context.Response.Close();

if (listener.IsListening)
listener.BeginGetContext(EndGetRequest, listener);
}
}

private void HandleRequest(HttpListenerContext context)
{
bool isScrape = context.Request.RawUrl.StartsWith("/scrape", StringComparison.OrdinalIgnoreCase);

BEncodedValue responseData = Handle(context.Request.RawUrl, context.Request.RemoteEndPoint.Address, isScrape);

byte[] response = responseData.Encode();
context.Response.ContentType = "text/plain";
context.Response.StatusCode = 200;
context.Response.ContentLength64 = response.LongLength;
context.Response.OutputStream.Write(response, 0, response.Length);
}

#endregion Methods
}
}


Start方法：首先new一个System.Net.HttpListener对象然后添加上URI然后调用System.Net.HttpListener对象的Start方法，再调用BeginGetContext开始异步检索传入的请求 listener.BeginGetContext(EndGetRequest, listener); 。异步操作通过EndGetRequest来完成。

HandleRequest是组织我们发给客户端的Response信息。内容是基类的Handle方法返回的一个BEncode编码的Dictionary转换成的字节数组。

Start具体调用层次如图

Stop方法：处理完当前请求队列后关闭Listener，将Listener置位null

### BitTorrent Protocol Specification

by jiang zhuo on 七.27, 2010, under BT协议

BitTorrent Protocol Specification

### BitTorrent 协议规范（BT协议集合）六

by jiang zhuo on 七.25, 2010, under BT协议

BT是如何采用激励机制来达到健壮性的
Bram Cohen
bram@bitconjurer.org
2003年5月22日

BitTorrent 文件发布系统采用针锋相对（tit_for_tat）的方法来达到帕累托有效，与当前已知的协作技术相比，它具有更高的活力。本文将解释BitTorrent 的用途，以及是怎样用经济学的方法来达到这个目标的。
1、BitTorrent 用来做什么？

1.1、BitTorrent接口
BitTorrent 的接口可能是最简单的。用户点击希望下载的文件的超级链接，然后会弹出一个标准的“保存到”对话框。此后，出现一个下载进度的窗口，在这个窗口中，除了显示下载速率外，还显示一个上载速率。BT在使用上非常简单，使得BT能广泛的被运用。
1.2、部署

2、技术框架
2.1发布内容

P2P的核心思想就是没有服务器的概念，任何一个下载者既是client，又是server。

2.2对等发布

Peers不断的从它能连接到的peers那里下载文件片断。当然，它不能从没有跟它建立连接的peers那里下载任何东西。即使是建立了连接的peers，有的也并不包含它想要的片断，或者还不允许它去下载。关于不允许其它peers从它那里下载文件片断的策略，被称为 阻塞choking，后文将进行讨论。其它关于文件分布的方法通常都要用到麻烦的树结构，而且树叶的上载能力并没有被利用起来。简单的让 peers 宣布它拥有什么会导致不到 10 % 的带宽开支，却可以可靠的使用所有的上载能力。
2.3流水作业

2.4片断选择

2.4.1严格的优先级

2.4.2最少的优先

2.4.3随机的第一个片断
“最少优先”的一个例外是在下载刚开始的时候。此时，下载者没有任何片断可供上传，所以，需要尽快的获取一个完整的片断。而最少的片断，通常只有某一个peer拥有，所以，它可能比多个peers都拥有的那些片断下载的要慢。因此，第一个片断是随机选择的，直到第一个片断下载完成，才切换到“最少优先”的策略。
2.4.4最后阶段模式

3 阻塞（choking）算法
BT并不集中分配资源。每个peer自己有责任来尽可能的提高它的下载速率。Peers从它可以连接的peers处下载文件，并根据对方提供的下载速率给予同等的上传回报（你敬我一尺，我敬你一丈）。对于合作者，提供上传服务，对于不合作的，就阻塞对方。所以说，阻塞是一种临时的拒绝上传策略，虽然上传停止了，但是下载仍然继续。在阻塞停止的时候，连接并不需要重新建立。

3.1帕累托有效

BitTorrent的阻塞算法，用一种针锋相对的方式来试图达到帕累托最优。（原文不太好翻译，我简化了）。Peers对那些向他提供上传服务的peers给予同样的回报，目的是希望在任何时候都有若干个连接正在进行着双向传输。
3.2 BitTorrent的阻塞算法

3.3、optimistic unchoking

3.4、反对歧视

3.5仅仅上传

4、真实世界的体验
BitTorrent不仅仅早已经实现，而且早已经被广泛的使用，它为许多并发的下载者提供成百兆的文件下载。已知的最大的部署中，同时有超过1000个的下载者。当前的瓶颈（实际还没有达到）看来是tracker的带宽。它（trakcer的带宽）大概占用了带宽总量的千分之一，一些小的协议扩展可能会使它降到万分之一。

[1] E. Adar and B. A. Huberman. Free riding on gnutella. First Monday, 5(10), 2000.
[2] A.-L. Barab´asi. Linked: The New Science of Networks.Perseus Publishing, 2002.
[3] M. Castro, P. Druschel, A.-M. Kermarrec, A. Nandi, A. Rowstron, and A. Singh. Splitstream: High-bandwidth content distribution in cooperative environments. In Proceedings of IPTPS03, Berkeley, USA, Feb. 2003.
[4] P. Maymounkov and D. Mazieres. Kademlia: A peer-to-peer information system based on the xormetric. In Proceedings of IPTPS02, Cambridge, USA, Mar. 2002.

### BitTorrent 协议规范（BT协议集合）四

by jiang zhuo on 七.25, 2010, under BT协议

这个协议很简单.
下面是实现它的封装类:

// UDPTrackerClient.h: interface for the CUDPTrackerClient class.
//
//
#if !defined(AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_)
#define AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include ＜WINSOCK2.H＞
#pragma comment(lib, "ws2_32.lib")
#ifndef _DISABLEWARNING4786_4355
#define _DISABLEWARNING4786_4355
#pragma warning( disable : 4786 )
#pragma warning( disable : 4355 )
#endif
#ifndef _ENABLEUSESTL
#define _ENABLEUSESTL
#include ＜LIST＞
#include ＜SET＞
#include ＜VECTOR＞
#include ＜QUEUE＞
#include ＜STRING＞
#include ＜MAP＞
using namespace std;
#endif
class CPeerHostInfo

public:
DWORD IP;//节点IP
WORD Port;//节点端口
};
class CUDPTrackerClient

public:
CUDPTrackerClient();
virtual ~CUDPTrackerClient();
void CancelSocketOperate();
BOOL Connect(const char * szServer,WORD wPort = 0);
DWORD Announcing(BYTE* pInfoHash,BYTE * pPeerID,
int ievent,
DWORD dwIP,WORD wPort);
BOOL Disconnect();
public:
SOCKET   m_socket;
DWORD   m_dwIP;
WORD   m_wPort;
__int64   m_iConnection_id;
DWORD   m_dwConnectTick;
string   m_strError; //如果请求失败,此变量保存错误信息
DWORD m_dwDonePeers; //种子数
DWORD m_dwNumPeers; //当前下载者个数
DWORD m_dwInterval; //查询间隔时间
list m_listPeers;
};
#endif // !defined(AFX_UDPTRACKERCLIENT_H__69B6ACC8_8193_4680_81D8_925B1550E92C__INCLUDED_)
// UDPTrackerClient.cpp: implementation of the CUDPTrackerClient class.
//
//
#include "stdafx.h"
#include "UDPTrackerClient.h"
#include "DataStream.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//
// Construction/Destruction
//
#define RECVBUFSIZE 2048
CUDPTrackerClient::CUDPTrackerClient()

m_socket = INVALID_SOCKET;
m_iConnection_id = 0;
m_dwConnectTick = 0;
m_dwIP = 0;
m_wPort = 0;
m_dwDonePeers = 0; //种子数
m_dwNumPeers = 0; //当前下载者个数
m_dwInterval = 0; //查询间隔时间

CUDPTrackerClient::~CUDPTrackerClient()

Disconnect();

void CUDPTrackerClient::CancelSocketOperate()

if(m_socket != INVALID_SOCKET)

LINGER lingerStruct;
// If we’re supposed to abort the connection, set the linger value
// on the socket to 0.
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
setsockopt(m_socket, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct) );

BOOL CUDPTrackerClient::Disconnect()

m_iConnection_id = 0;
m_dwDonePeers = 0; //种子数
m_dwNumPeers = 0; //当前下载者个数
m_dwInterval = 0; //查询间隔时间
if ( m_socket != INVALID_SOCKET )

m_dwIP = 0;
m_wPort = 0;
// Now close the socket handle. This will do an abortive or
// graceful close, as requested.
shutdown(m_socket,SD_BOTH);
closesocket(m_socket);
m_socket = INVALID_SOCKET;
return TRUE;

return FALSE;

//szServer连接的主机,可以是下列形式的字符串:
//easeso.com:1000
//easeso.com
//如果wPort不为0,则szServer不应该包含端口信息
BOOL CUDPTrackerClient::Connect(const char * szServer,WORD wPort)

m_strError = "";
BOOL bRes = FALSE;
if ( m_socket == INVALID_SOCKET )

//用UDP初始化套接字
BOOL optval = TRUE;
m_socket =socket(AF_INET,SOCK_DGRAM,0);
if(m_socket == INVALID_SOCKET)
return FALSE;
//设置超时时间
int TimeOut=10000;
int err = setsockopt (m_socket, SOL_SOCKET,SO_RCVTIMEO,(CHAR *) &TimeOut,sizeof (TimeOut));

if(m_dwIP == 0)

CString strServer = szServer;
CString strHost;
if(wPort == 0)

int iNext = strServer.Find(‘:’);
if(iNext>0)
{
strHost = strServer.Mid(0,iNext);
CString strPort = strServer.Mid(iNext+1);
m_wPort = (WORD)atoi(strPort);
}
else
strHost = strServer;

else

strHost = strServer;
m_wPort = wPort;

if(m_wPort == 0)
m_wPort = 80;
//Check if address is an IP or a Domain Name
int a = strHost[0];
if (a > 47 && a < 58)
else

struct hostent *pHost;
pHost = gethostbyname(strHost);
if(pHost != NULL)
else
m_dwIP = 0;

if((GetTickCount()-m_dwConnectTick)>30000)

m_dwConnectTick = 0;
m_iConnection_id = 0;

if(m_socket != INVALID_SOCKET && m_dwIP && m_wPort && m_iConnection_id ==0)

DWORD dwTransaction_id = GetTickCount();
char buf[RECVBUFSIZE];
from.sin_family=AF_INET;
from.sin_port=htons(m_wPort);
CDataStream sendstream(buf,2047);
sendstream.clear();
__int64 iCID = 0×41727101980;
sendstream.writeint64(CNetworkByteOrder::convert(iCID));
sendstream.writedword(CNetworkByteOrder::convert((int)0));
sendstream.writedword(dwTransaction_id);
int iRes = 0;
int iTimes = 6;
while(iTimes>0&&m_dwIP)

iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength);
if(iRes >=0)
break;
iTimes–;

if(iRes>=16)

CDataStream recvstream(buf,RECVBUFSIZE-1);
if(dwTIDResp == dwTransaction_id)
{
if(dwAction == 0)
{
//BitComet将回复0×16字节数据,最后6字节是服务器查看到的本地IP和UDP端口
}
else if(dwAction == 3)//得到一个错误信息包
{
buf[iRes]=0;
}
}

if(m_iConnection_id)
bRes = TRUE;
return bRes;

//提交请求
//pInfoHash 20字节的数据缓冲区指针
//pPeerID 20字节的数据缓冲区指针
//ievent参数值:
//none = 0
//completed = 1
//started = 2
//stopped = 3
DWORD CUDPTrackerClient::Announcing(BYTE* pInfoHash,BYTE * pPeerID,
int ievent,
DWORD dwIP,WORD wPort)

m_listPeers.clear();
m_dwNumPeers = 0;
m_dwDonePeers = 0;
m_strError = "";
DWORD dwReturnCode = 0;
if(m_iConnection_id && m_socket != INVALID_SOCKET && m_dwIP & m_wPort)

DWORD dwTransaction_id = GetTickCount();
//srand(dwTransaction_id);
//DWORD dwKey = rand();
DWORD dwKey = 0×3753;
char buf[RECVBUFSIZE];
from.sin_family=AF_INET;
from.sin_port=htons(m_wPort);
CDataStream sendstream(buf,RECVBUFSIZE-1);
sendstream.clear();
sendstream.writeint64(m_iConnection_id);
sendstream.writedword(CNetworkByteOrder::convert((int)1));
sendstream.writedword(dwTransaction_id);
sendstream.writedata(pInfoHash,20);
sendstream.writedata(pPeerID,20);
sendstream.writeint64(CNetworkByteOrder::convert(ileft));
sendstream.writedword(CNetworkByteOrder::convert(ievent));
sendstream.writedword(dwIP);
sendstream.writedword(CNetworkByteOrder::convert((int)dwKey));
sendstream.writedword(CNetworkByteOrder::convert((int)200));
sendstream.writedword(CNetworkByteOrder::convert(wPort));
int iRes = 0;
int iTimes = 2;
while(iTimes>0&&m_dwIP)

iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength);
if(iRes >=0)
break;
iTimes–;

if(iRes>=20)
{
CDataStream recvstream(buf,RECVBUFSIZE-1);
if(dwTIDResp == dwTransaction_id)
{
if(dwAction == 1)
{
CPeerHostInfo hi;
for(int iCurPos = 20;iCurPos+6<=iRes;iCurPos+=6)
{
m_listPeers.push_back(hi);
}
if(m_dwNumPeers>m_listPeers.size())
{
iRes = 0;
iTimes = 6;
while(iTimes>0&&m_dwIP)
{
iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength);
if(iRes >=0)
break;
iTimes–;
}
if(iRes>=6)
{
for(iCurPos = 0;iCurPos+6<=iRes;iCurPos+=6)
{
m_listPeers.push_back(hi);
}
}
}
m_dwNumPeers = m_listPeers.size();
dwReturnCode = 200;
}
else if(dwAction == 3)//得到一个错误信息包
{
buf[iRes]=0;
dwReturnCode = 400;
}
}

//每次都要求重新连接
m_iConnection_id = 0;
return dwReturnCode;

// DataStream.h: interface for the CDataStream class.
//
//
#if !defined(AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_)
#define AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include
//数据流操作函数
class CDataStream

public :
CDataStream(char * szBuf,int isize)

m_isize = isize;
buffer = szBuf;
current = buffer;

~CDataStream()

void clear()

current = buffer;
current[0]=0;

//此函数不动态增加内存,一次打印的数据长度不应该超过缓冲区的三分之一,否则可能导致失败
bool printf(const char * format,…)

if(current)

if(current – buffer > (m_isize*2)/3)
return false;
va_list argPtr ;
va_start( argPtr, format ) ;
int count = vsprintf( current, format, argPtr ) ;
va_end( argPtr );
current += count ;
return true;

return false;

//此函数拷贝字符串
bool strcpy(const char * szStr)

if(current&&szStr)

int ilen = lstrlen(szStr);
if((m_isize-(current – buffer)) < (ilen +2))
return false;
memcpy(current,szStr,ilen+1);
current += ilen;
return true;

return false;

char * getcurrentpos()

return current;

void move(int ilen)//当前指针向后移动ilen

current += ilen;

void reset()

current = buffer;

current ++;
return *(current-1);

void writebyte(BYTE btValue)

*current = btValue;
current ++;

current +=2;
return *((WORD*)(current-2));

void writeword(WORD wValue)

*((WORD*)current) = wValue;
current +=2;

current +=4;
return *((DWORD*)(current-4));

void writedword(DWORD dwValue)

*((DWORD*)current) = dwValue;
current +=4;

current +=8;
return *((__int64*)(current-8));

void writeint64(__int64 iValue)

*((__int64*)current) = iValue;
current +=8;

current +=dwLen;
return (BYTE*)(current-dwLen);

void writedata(BYTE * pData,DWORD dwLen)

memcpy(current,pData,dwLen);
current +=dwLen;

char * szRes = current;
int ilen = lstrlen(current);
current +=(ilen+1);
return szRes;

int size()

return (int)(current-buffer);

const char * getbuffer(){return buffer;}
private :
char* buffer;
char* current;
int m_isize;
};
class CNetworkByteOrder

public:
static unsigned short int convert(unsigned short int iValue)

unsigned short int iData;
((BYTE*)&iData)[0] = ((BYTE*)&iValue)[1];
((BYTE*)&iData)[1] = ((BYTE*)&iValue)[0];
return iData;

static int convert(int iValue)

int iData;
((BYTE*)&iData)[0] = ((BYTE*)&iValue)[3];
((BYTE*)&iData)[1] = ((BYTE*)&iValue)[2];
((BYTE*)&iData)[2] = ((BYTE*)&iValue)[1];
((BYTE*)&iData)[3] = ((BYTE*)&iValue)[0];
return iData;

static __int64 convert(__int64 iValue)

__int64 iData;
((BYTE*)&iData)[0] = ((BYTE*)&iValue)[7];
((BYTE*)&iData)[1] = ((BYTE*)&iValue)[6];
((BYTE*)&iData)[2] = ((BYTE*)&iValue)[5];
((BYTE*)&iData)[3] = ((BYTE*)&iValue)[4];
((BYTE*)&iData)[4] = ((BYTE*)&iValue)[3];
((BYTE*)&iData)[5] = ((BYTE*)&iValue)[2];
((BYTE*)&iData)[6] = ((BYTE*)&iValue)[1];
((BYTE*)&iData)[7] = ((BYTE*)&iValue)[0];
return iData;

};
#endif // !defined(AFX_DATASTREAM_H__D90A2534_EA73_4BEA_8B7E_87E59A3D1D26__INCLUDED_)

### BitTorrent 协议规范（BT协议集合）三

by jiang zhuo on 七.25, 2010, under BT协议

Bittorrent udp-tracker protocol extension
Contents
introduction
connecting
Client sends packet:
Server replies with packet:
announcing
Client sends packet:
Server replies with packet:
scraping
Client sends packet:
Server replies with packet:
errors
server replies packet:
actions
extensions
authentication
credits
introduction
A tracker with the protocol "udp://" in its URI is supposed to be contacted using this protocol.
This protocol is supported by xbt-tracker.
For additional information and descritptions of the terminology used in this document, see the protocol specification
All values are sent in network byte order (big endian). The sizes are specified with ANSI-C standard types.
If no response to a request is received within 15 seconds, resend the request. If no reply has been received after 60 seconds, stop retrying.
connecting
Client sends packet:size name description
int64_t connection_id Must be initialized to 0×41727101980 in network byte order. This will identify the protocol.
int32_t action 0 for a connection request
int32_t transaction_id Randomized by client.
Server replies with packet:size name description
int32_t action Describes the type of packet, in this case it should be 0, for connect. If 3 (for error) see errors.
int32_t transaction_id Must match the transaction_id sent from the client.
int64_t connection_id A connection id, this is used when further information is exchanged with the tracker, to identify you. This connection id can be reused for multiple requests, but if it’s cached for too long, it will not be valid anymore.
announcing
Client sends packet:size name description
int64_t connection_id The connection id acquired from establishing the connection.
int32_t action Action. in this case, 1 for announce. See actions.
int32_t transaction_id Randomized by client.
int8_t[20] info_hash The info-hash of the torrent you want announce yourself in.
int64_t left The number of bytes you have left to download until you’re finished.
int64_t uploaded The number of bytes you have uploaded in this session.
int32_t event The event, one of
none = 0
completed = 1
started = 2
stopped = 3
uint32_t ip Your ip address. Set to 0 if you want the tracker to use the sender of this udp packet.
uint32_t key A unique key that is randomized by the client.
int32_t num_want The maximum number of peers you want in the reply. Use -1 for default.
uint16_t port The port you’re listening on.
uint16_t extensions See extensions
Server replies with packet:size name description
int32_t action The action this is a reply to. Should in this case be 1 for announce. If 3 (for error) see errors. See actions.
int32_t transaction_id Must match the transaction_id sent in the announce request.
int32_t interval the number of seconds you should wait until reannouncing yourself.
The rest of the server reply is a variable number of the following structure:
size name description
int32_t ip The ip of a peer in the swarm.
uint16_t port The peer’s listen port.
scraping
Client sends packet:size name description
int64_t connection_id The connection id retreived from the establishing of the connection.
int32_t action The action, in this case, 2 for scrape. See actions.
int32_t transaction_id Randomized by client.
int16_t num_info_hashes The number of info-hashes that will follow.
uint16_t extensions See extensions.
The following structure is repeated num_info_hashes times:
size name description
int8_t[20] info_hash The info hash that is to be scraped.
Server replies with packet:size name description
int32_t action The action, should in this case be 2 for scrape. If 3 (for error) see errors.
int32_t transaction_id Must match the sent transaction id.
The rest of the packet contains the following structures once for each info-hash you asked in the scrape request.
size name description
int32_t incomplete The current number of connected leechers.
errors
In case of a tracker error,
server replies packet:size name description
int32_t action The action, in this case 3, for error. See actions.
int32_t transaction_id Must match the transaction_id sent from the client.
int8_t[] error_string The rest of the packet is a string describing the error.
actions
The action fields has the following encoding:
connect = 0
announce = 1
scrape = 2
error = 3 (only in server replies)
extensions
The extensions field is a bitmask. The following bits are assigned:
1 = authentication.
authenticationThe packet will have an authentication part appended to it. It has the following format:
size name description
int8_t[] username The username, the number of characters as specified in the previous field.
uint8_t[8] passwd_hash sha1(packet + sha1(password)) The packet in this case means the entire packet except these 8 bytes that are the password hash. These are the 8 first bytes (most significant) from the 20 bytes hash calculated.
credits
Protocol designed by Olaf van der Spek

### BitTorrent 协议规范（BT协议集合）二

by jiang zhuo on 七.25, 2010, under 转载

BitTorrent协议详解
BitTrrent(简称BT，比特洪流)是一个文件分发协议，它通过URL识别内容并且和网络无缝结合。它在HTTP平台上的优势在于，同时下在一个文件的下载者在下载的同时不断互相上传数据，使文件源可以在很有限的负载增加的情况下支持大量下载者同时下载。

·一个普通网络服务器
·一个静态元信息文件
·一个BT Tracker
·一个“原始”下载者
·网络终端浏览者
·网络终端下载者

1.开始运行Tracker（已运行的跳过这一步）；
2.开始运行普通网络服务器端程序，如Apache，已运行的跳过这一步；
3.在网络服务器上将.torrent文件关联到Mimetype类型application/x-bittorrent（已关联的跳过这一步）；
4.用要发布的完整文件和Tracker的URL创建一个元信息文件（.torrent文件）；
5.将元信息文件放置在网络服务器上；
6.在网页上发布元信息文件（.torrent文件）链接；
7.原始下载者提供完整的文件（原本）。

1.安装BT客户端程序（已安装的跳过这一步）；
2.上网；
3.点击一个链到.torrent文件的链接；
4.选择本地存储路径，选定需要下载的文件（对有选择下载功能的BT客户端用户）；
5.等待下载完成；
6.用户退出下载（之前下载者不停止上传）。

·网站正常提供静态文件连接，并且启动客户端上的BT程序；
·Tracker即时接收所有下载者信息，并且给每个下载者一份随机的peer列表。通过HTTP或HTTPS协议实现；
·下载者每隔一段时间连一次Tracher，告知自己的进度，并和那些已经直接连接上的peer进行数据的上传下载。这些连接遵循BitTorrent peer协议，通过TCP协议进行通信。
·原始下载者只上传不下载，他拥有整个文件，所以很必要向网络中传输完文件的所有部分。在一些人气很旺的下载中，原始下载者经常可以在较短的时间内退出上传，由其它已经下载到整个文件的下载者继续提供上传。

B编码规则如下：
·字符串表示为十进制数的既定字符串长度加冒号再跟原字符串。

·整型数据表示成前面加’i'后面加’e'中间是十进制数，如i3e就相当于3，i-3e就是-3。整型数据没有长度限制。i-0e无效，所有以’i0′开头的除了代表0的i0e，其它都无效。
·列表编码为一个’l'开头后面跟它所包含的项目（已经编码过）最后加一个’e'，比如l4:spam4:eggse就等于['spam', 'eggs']。
·字典编码为一个’d'开头后面跟一个交替关键值（key）及其对应值的列表最后加一个’e'。

d4:spaml1:a1:bee相当于{‘spam’: ['a', 'b']}

announce（声明）
Tracker的URL。
info（信息）

length（长度）

path（路径）

（一个长度为零的length表单是错误的。）

Tracker质询是双向的。Tracker通过HTTP GET参数获得信息，然后返回一个B编码后的信息。尽管Tracker需要在服务器端执行，但它运行流畅像Apache的一个模块。
Tracker的GET请求有如下关键值：
info_hash
20字节长的SHA1验证码，来自B编码过的元信息文件中的info值下，是元信息文件的一个支链。这个值是自动转换的。
peer_id

ip

port

left

event

Tracker的回应也是B编码字典。如果Tracker回应中有关键值failure reason（失败原因），就会对应一个人可以读懂的字符串信息解释质询失败的原因，不需要其它关键值。否则，回应必须有两个关键值：interval（间隔）对应下载者定期发出请求的间隔秒数；peers，peer自选ID，IP地址或DNS主机名的字符串和端口号。记住peers不会完全按照计划的间隔发送请求，假如他们发生一个事件或者想要更多的peers。

BitTorrent peer协议通过TCP协议进行操作。它不用调节任何socket选项就可以流畅运行。
peer之间的连接是对称的。两个方向送出的信息要协调一致，数据可以流入任一方。
peer协议指一个peer从零开始下载，每得到元信息文件索引中所描述的一个块且验证码一致，就向所有peer声明已得到此块。

peer连线协议包括一次握手跟着不断的大小一致且确定的信息流。握手的开始是字符十九（十进制），跟着是字符串’BitTorrentprotocol’。开头的字符是长度固定的，希望其它新协议也能这样以便区分。

·0-阻塞
·1-通畅
·2-关注
·3-不关注
·4-有
·5-比特组
·6-请求
·7-块
·8-取消
“阻塞”、“通畅”、“关注”和“不关注”类信息没有荷载。
“比特组”类信息仅作为首信息发出。它负载一个比特组，下载者有索引的设为1，其它为0。开始下载时没有任何数据的下载者跳过“比特组”信息。首字节高位到低位对应索引0-7，依次类推，第二字节对应8-15，等等。尾部的剩余的比特位设为0。
“已有”类信息负载一个数，即刚下载并核对完验证码的索引数。
“请求”类信息包括包含一个索引，开始和长度。后两者是字节偏移。长度一般是2的权值除非被文件尾截断。现行一般是2的15次幂，并且关闭大于2的17次幂长度的连接。
“取消”类信息负载和“请求”类信息有一样的负载。它通常在下载接近完成即“最后阶段”发出。当下载快要完成时，剩下几个块有都从同一个线程下载的趋向，这样会很慢。为了确保剩余块下载迅速，一旦还没有决定剩余块的下载请求向谁发出，先向所有他正在从对方下载数据的连接者发送要求所有剩余块的请求。为避免低效，每当一个块开始下载就向其他peer发出取消信息。
“块”类信息包含一个索引，开始和块。记住它和“请求”类信息是相关的。当传输速度很慢或“阻塞”“通畅”类信息高频率交替发出或两者同时发生，可能会载到一个不需要的块。

### BitTorrent 协议规范（BT协议集合）一

by jiang zhuo on 七.25, 2010, under BT协议

BitTorrent 是一种分发文件的协议。它通过URL来识别内容，并且可以无缝的和web进行交互。它基于HTTP协议，它的优势是：如果有多个下载者并发的下载同一个文件，那么，每个下载者也同时为其它下载者上传文件，这样，文件源可以支持大量的用户进行下载，而只带来适当的负载的增长。（译注：因为大量的负载被均衡到整个系统中，所以提供源文件的机器的负载只有少量增长）

Ø运行一个 tracker服务器（或者，已经有一个tracker服务器在运行了也可以）
Ø运行一个web服务器，例如apache，或者已经有一个web服务器在运行了。
Ø在web服务器上，将文件扩展名.torrent 和MIME类型 application/x-bittorrent关联起来（或者已经关联了）
Ø根据 tracker服务器的 URL 和要共享的文件来创建一个“元信息”文件（.torrent）。
Ø将“元信息”文件发布到web服务器上
Ø在某个web页面上，添加一个到“元信息”文件的链接。
Ø运行一个已经拥有完整文件的下载者（被成为’origin’，或者’seed’，种子）

Ø安装 BT（或者已经安装）
Ø访问提供 .torrent 文件的web服务器
Ø点击到 .torrent 文件的链接（译注：这时候，bt会弹出一个对话框）
Ø选择要把下载的文件保存到哪里？或者是一次断点续传
Ø等待下载的完成。
Ø结束bt程序的运行（如果不主动结束，那么bt会一直为其它人提供文件上传）

Trackers从所有下载者处接收信息，并返回给它们一个随机的peers的列表。这种交互是通过HTTP或HTTPS协议来完成的。

Origin只负责上传，从不下载，因为它已经拥有了完整的文件。Origin是必须的。

Bencoding格式如下：

announce tracker的服务器
info 它实际上是一个字典，包括以下关键字：
Name：

Piece length：

Pieces：

Length：文件长度
Path：子目录名称的列表，列表最后一项是文件的实际名称。（不允许出现列表为空的情况）。
Name：在单文件情况下，name是文件的名称，而在多文件情况下，name是目录的名称。
Tracker查询。Trakcer通过HTTP的GET命令的参数来接收信息，而响应给对方（也就是下载者）的是经过bencoded编码的消息。注意，尽管当前的tracker的实现需要一个web服务器，它实际上可以运行的更轻便一些，例如，作为apache的一个模块。
Tracker GET requests have the following keys:

Info_hash：

Peer_id：

Ip：

Port：
peer所监听的端口。下载者通常在在 6881 端口上监听，如果该端口被占用，那么会一直尝试到 6889，如果都被占用，那么就放弃监听。

Left：

Event：

Tracker的响应是用bencoded编码的字典。如果tracker的响应中有一个关键字failure reason，那么它对应的是一个字符串，用来解释查询失败的原因，其它关键字都不再需要了。否则，它必须有两个关键字：Interval：下载者在两次发送请求之间的时间间隔。Peers:一个字典的列表，每个字典包括以下关键字：Peer id，Ip，Port，分别对应peer所选择的id、ip地址或者dns名称、端口号。注意，如果某些事件发生，或者需要更多的peers，那么下载者可能不定期的发送请求，

BT对等协议基于TCP，它很有效率，并不需要设置任何socket选项。（译注：BT对等协议指的是peer与peer之间交换信息的协议）

‘choke’, ‘unchoe’, ‘interested’, not interested’类型的消息不再含有其它数据了。
‘have’类型的消息，后面的数据是一个简单的数字，它是下载者刚刚下载完并检查过完整性的片断的索引。（由此，可以看到，peer通过这种消息，很快就相互了解了谁都有什么片断）
‘request’类型的消息，后面包含索引、开始位置和长度)长度是2的幂。当前的实现都用的是215 ，而关闭连接的时候，请求一个超过2 17的长度。(这种类型的消息，就是当一个peer希望另一个peer给它提供片断的时候，发出的请求)
‘cancel’类型的消息，它的数据和’request’消息一样。它们通常只在下载趋向完成的时候发送，也就是在‘结束模式“阶段发送。在一次下载接近完成的时候，最后的几个片断需要很长时间才能下载完。为了确保最后几个片断尽快下载完，它向所有的peers发送下载请求。为了保证这不带来可怕的低效，一旦某个片断下载完成，它就其它peers发送’cancel’消息。（意思就是说，我不要这个片断了，你要是准备好了，也不用给我发了，可以想象，如果对方还是把数据发送过来了，那么这边必须忽略这些重复的数据）。
‘piece’类型的消息，后面保护索引号、开始位置和实际的数据。注意，这种类型的消息和 ‘request’消息之间有潜在的联系（译注：因为通常有了request消息之后，才会响应‘piece’消息）。如果choke和unchoke消息发送的过于迅速，或者，传输速度变的很慢，那么可能会读到一些并不是所期望的片断。（ 也就是说，有时候读到了一些片断，但这些片断并不是所想要的）

• 0
点赞
• 3
收藏
觉得还不错? 一键收藏
• 0
评论
07-30 3515
03-12 7万+
08-18
01-24
04-06 3038
05-22
06-16
12-27 628
08-08 210
09-20 2万+
08-11 2631
10-10 1399

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。