最近很忙,自上次Blog被盜 帖子所有丟失後也不多時間更新Blog了,閒暇在國外站點查閱資料時正好看到一些Tracker 的協議資料,也就今天記錄並實踐了下,再次分享給你們但願能夠幫到須要的小夥伴。算法
首先咱們來了解下BT Trackersql
1、作種數據庫
如今不少BT軟件都提供了作種功能,在作種時,咱們都必須指定tracker服務器地址,若是該地址無效,則作出來的種子對BT協議來講是沒有任何實際意義的。api
2、bt tracker服務瀏覽器
對於純BT協議來講,每一個BT網絡中至少要有一臺Tracker服務器(追蹤服務器),tracker主要基本工做有如下幾個方面:服務器
每次咱們利用BT軟件作完種子後,總要找個論壇之類的來上傳本身的種子,這樣別人就能夠下載到這個種子。爲何要上傳種子呢?緣由:網絡
當其餘用戶用BT軟件打開種子後,BT軟件會對種子進行解析(BDecode),主要獲得種子的相關信息,包括:文件名、文件大小、tracker地址等。而後BT軟件會向tracker地址發送請求報文,開始進行下載。BT向tracker發送的是Get請求,請求的內容主要有如下幾個方面:ide
info_hash測試 |
必填ui |
種子文件info字段的SHA1值(20字節) |
peer_id |
必填 |
節點標識,由BT客戶端每次啓動時隨機生成 |
port |
必填 |
節點端口,主要用於跟其餘節點交互 |
uploaded |
必填 |
總共上傳的字節數,初始值爲0 |
downloaded |
必填 |
總共下載的字節數,初始值爲0 |
left |
必填 |
文件剩餘的待下載字節數 |
numwant |
必填 |
BT客戶端指望獲得的節點數 |
ip |
選填 |
BT客戶端IP,選填的緣由是Tracker能夠獲得請求的IP地址,不須要客戶端直接上傳 |
event |
選填 |
started/stopped/completed/空。當BT客戶端開始種子下載時,第一個發起的請求爲started, 在下載過程當中,該值一直爲空,直到下載完成後才發起completed請求。作種過程當中,發送 的event也爲空。若是BT客戶端中止作種或退出程序,則會發起stopped請求。 |
tracker收到該請求後主要進行如下幾步處理:
1. 根據info_hash查找種子信息,若是tracker沒有該種子的任何信息,tracker服務器能夠返回錯誤或返回0個種子數
2. 若是tracker找到了種子信息,接下來就會去查找是否數據庫中已存在該peer_id的節點。接下來根據event的值進行相關處理。
3. 若是event是stopped,說明該節點已不可用,系統會刪除tracker上關於該節點的記錄信息。
4. 若是event是completed,說明種子節點+1,非種子-1。
5. 若是event是started,說明這是種子第一次鏈接tracker,tracker須要記錄該節點信息,此外若是left=0,說明這是一個種子節點。
6. 若是event是空,則說明節點正在下載或上傳,須要更新tracker服務器上該節點的信息。
7. 最後tracker從本地挑選出numwant個節點信息返回給BT客戶端,實際返回的節點數不必定就是numwant,tracker只是儘可能達到這個數量。
Tracker響應
Tracker正常返回的信息結構主要是:
interval |
必填 |
請求間隔(秒) |
|
complete |
選填 |
種子節點數 |
|
Incomplete |
選填 |
非種子節點數 |
|
peers |
ip |
必填 |
IP地址 |
peer_id |
選填 |
節點標識 |
|
port |
必填 |
端口 |
若是Tracker檢查發現異常,能夠返回錯誤信息:
failure reason |
錯誤緣由 |
Tracker如何挑選種子節點並返回給客戶端?
最廣泛也是最簡單的方式,那就是隨機返回,tbsource採用的就是隨機返回的機制。很多研究論文也提出了相關的算法,如IP地址策略和階段返回策略。
IP地址策略是指根據IP地址所含拓撲信息來判斷兩個節點的距離,從而返回距離請求節點較近的節點列表。該方法主要適用於IPV6。
階段返回策略,根據節點的下載進度,返回下載進度相近的節點列表。
我的觀點:不管tracker採用什麼算法,對BT客戶端來講,可以提升的下載效率都是頗有限的,採用「高級」的算法有時反而會增長tracker的負載。所以隨機返回還算是比較高效的。
Bt協議中,有兩個策略能夠用來提升整個BT網絡的健壯性和下載速度,它們分別是:最少片斷優先策略(BT客戶端處理)和最後階段模式。爲了響應「最後階段模式」,當種子節點的下載進度大於80%(我的指定)時,tracker服務器應該儘可能返回種子節點給客戶端,幫助客戶端儘快完成下載,使其成爲種子節點。
3、private tracker原理
Privatetracker簡稱PT,目前主要應用於高清視頻下載。其實PT就是「我爲人人,人人爲我」這個目標的最佳實踐者。在實際的BT下載過程當中,用戶經過種子下載完文件後,出於「自私」的考慮(怕佔用本身帶寬),每每會退出作種,從而下降種子的熱度。這就是爲何一個種子過了一段時間後,每每下載速度很慢或下載不完。
爲了真正地實現BT理念,PT強制每一個下載者必須上傳必定量數據後,才能進行下載。如何保證這種行爲呢?
如今的PT通常存在於網絡社區中,每一個註冊網絡社區的用戶都會分配到一個隨機的KEY,任何從社區下載的種子,都會包含用戶的KEY。每次用戶經過種子下載時,都會鏈接到社區的tracker服務器上,tracker服務器會檢查KEY對應用戶的上傳下載量,若是上傳量不知足標準,則tracker服務器會記錄相關信息,並對該用戶的下載及社區活動進行相關限制。
瞭解的基礎的一些原理後 咱們從實踐開始入手:
封裝Tracker類及數據請求上下文:
namespace WebApplication8 { public class TrackerContext : DbContext { public DbSet<Tracker> Bittorrents { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=Tracker.db"); } } public class Tracker { public int Id { get; set; } //InfoHash public string InfoHash { get; set; } //PeerId public string PeerId { get; set; } //客戶端的IP地址 public string Ip { get; set; } //客戶端的端口 public int Port { get; set; } //上傳的字節數量 public int Uploaded { get; set; } //下載的字節數量 public int Downloaded { get; set; } //文件剩餘的待下載字節數 public int Left { get; set; } //客戶端 事件 started/stopped/completed/空 public string Event { get; set; } } }
服務端 簡單實現:
namespace WebApplication8.Controllers { [Route("[controller]")] [ApiController] public class announceController : ControllerBase { private TrackerContext _db; public announceController(TrackerContext db) { _db = db; } // GET api/values [HttpGet] public string Get() { try { //?info_hash=o%b8t%7c~%e86%fc2%878%5c%f5%fbj0%40%26-a&peer_id=-UT354S-%e8%ad%86%f5%0d%ee%86%40%9aXo%f9&port=53974&uploaded=0&downloaded=0&left=0&corrupt=0&key=E96680BC&event=started&numwant=200&compact=1&no_peer_id=1 var dic = GetDic(Request.QueryString.ToString()); var infoHash = BitConverter.ToString(HttpUtility.UrlDecodeToBytes(dic["@info_hash"].ToString())).Replace("-", "").ToLower(); var peer_id = BitConverter.ToString(HttpUtility.UrlDecodeToBytes(dic["@peer_id"].ToString())).Replace("-", "").ToLower(); //判斷是否存在該tracker var entity = _db.Bittorrents.FirstOrDefault(p => p.InfoHash == infoHash && p.PeerId == peer_id); //不存在插入tracker信息 if (entity == null) { _db.Bittorrents.Add(new Tracker { InfoHash = infoHash, Ip = Request.HttpContext.Connection.RemoteIpAddress.ToString(), Left = Convert.ToInt32(dic["@left"]), Uploaded = Convert.ToInt32(dic["@uploaded"]), Downloaded = Convert.ToInt32(dic["@downloaded"]), Event = dic["@event"].ToString(), PeerId = peer_id, Port = Convert.ToInt32(dic["@port"]) }); _db.SaveChanges(); } else { //存在更新Tracker信息 entity.Ip = Request.HttpContext.Connection.RemoteIpAddress.ToString(); entity.Uploaded = Convert.ToInt32(dic["@uploaded"]); entity.Downloaded = Convert.ToInt32(dic["@downloaded"]); entity.Left = Convert.ToInt32(dic["@left"]); entity.Port = Convert.ToInt32(dic["@port"]); entity.Event = dic.ContainsKey("@event") ? dic["@event"].ToString() : null; _db.SaveChanges(); } dic.Clear(); //構造tracker信息列表 返回給客戶端 interval 客戶端心跳請求間隔 單位:秒 會間隔後自動心跳上報客戶端的信息 dic.Add("interval", 60); List<object> peers = new List<object>(); _db.Bittorrents.Where(p => p.InfoHash == infoHash).ToList().ForEach(o => { SortedDictionary<string, object> peer = new SortedDictionary<string, object>(StringComparer.Ordinal); peer.Add("peer id", o.PeerId); peer.Add("ip", o.Ip); peer.Add("port", o.Port); peers.Add(peer); }); dic.Add("peers", peers); return encode(dic); } catch (Exception) { throw new Exception("請遵循Tracker協議,禁止瀏覽器直接訪問"); } } public SortedDictionary<string, object> GetDic(string query) { string s = query.Substring(1); SortedDictionary<string, object> parameters = new SortedDictionary<string, object>(StringComparer.Ordinal); int num = (s != null) ? s.Length : 0; for (int i = 0; i < num; i++) { int startIndex = i; int num4 = -1; while (i < num) { char ch = s[i]; if (ch == '=') { if (num4 < 0) { num4 = i; } } else if (ch == '&') { break; } i++; } string str = null; string str2 = null; if (num4 >= 0) { str = s.Substring(startIndex, num4 - startIndex); str2 = s.Substring(num4 + 1, (i - num4) - 1); } else { str2 = s.Substring(startIndex, i - startIndex); } parameters.Add("@" + str, str2); } return parameters; } public string encode(string _string) { StringBuilder string_builder = new StringBuilder(); string_builder.Append(_string.Length); string_builder.Append(":"); string_builder.Append(_string); return string_builder.ToString(); } public string encode(int _int) { StringBuilder string_builder = new StringBuilder(); string_builder.Append("i"); string_builder.Append(_int); string_builder.Append("e"); return string_builder.ToString(); } public string encode(List<object> list) { StringBuilder string_builder = new StringBuilder(); string_builder.Append("l"); foreach (object _object in list) { if (_object.GetType() == typeof(string)) { string_builder.Append(encode((string)_object)); } if (_object.GetType() == typeof(int)) { string_builder.Append(encode((int)_object)); } if (_object.GetType() == typeof(List<object>)) { string_builder.Append(encode((List<object>)_object)); } if (_object.GetType() == typeof(SortedDictionary<string, object>)) { string_builder.Append(encode((SortedDictionary<string, object>)_object)); } } string_builder.Append("e"); return string_builder.ToString(); } public string encode(SortedDictionary<string, object> sorted_dictionary) { StringBuilder string_builder = new StringBuilder(); string_builder.Append("d"); foreach (KeyValuePair<string, object> key_value_pair in sorted_dictionary) { string_builder.Append(encode((string)key_value_pair.Key)); if (key_value_pair.Value.GetType() == typeof(string)) { string_builder.Append(encode((string)key_value_pair.Value)); } if (key_value_pair.Value.GetType() == typeof(int)) { string_builder.Append(encode((int)key_value_pair.Value)); } if (key_value_pair.Value.GetType() == typeof(List<object>)) { string_builder.Append(encode((List<object>)key_value_pair.Value)); } if (key_value_pair.Value.GetType() == typeof(SortedDictionary<string, object>)) { string_builder.Append(encode((SortedDictionary<string, object>)key_value_pair.Value)); } } string_builder.Append("e"); return string_builder.ToString(); } } }
Tracker 地址http://192.168.50.11:5000/announce 我是在本地部署進行了測試
sqlite數據庫種的Tracker信息:
在個人另外一臺Nas進行下載測試並輔種測試:
至此我進行了作種下載測試均一切正常,若是你們在閱讀此文有疑問之處還及不足之處望留言 再次感謝閱讀。