トレントファイル。 中身は何ですか?

はじめに



画像

こんにちは

私は、多くのように、大きなトレントトラッカーrutracker.orgを使用しますが、私を悩ます1つの機能があります。

これにより、トラッカーのリストにアドレスix *。Rutracker.netが追加されますが、これは私にはわかりにくい目的に役立ちます。 ただし、多くの場合(ほとんどの場合-ほとんど常に)エラー( 502 Bad Gatewayおよび0 No Response )がスローされます。 トレントクライアント(私はTransmissionを持っています)は、トレントが壊れているとマークします。 もちろん、これはかなり気になります。 特に、送信機能を検討する場合は、トラッカーの最後の応答に従ってトレントのステータスを設定します。 つまり、ix *をポーリングするとエラーが返され、トレントはBrokenとしてマークされます。n分/秒後にリストの次のトラッカーがポーリングされます-bt * .rutracker.orgまたはretracker.local 。正常なコードを返し、トレントは再び正常になります。 この混乱は私にはあまり満足していません。



解決策は平凡です-この悪いアドレスをリストから削除してください。 ただし、ファイルがたくさんあるため、各ファイルを手動で切り取りたくはありません。また、新しいトレントを追加するときに追加のアクションを実行する必要はありません。 したがって、フォーマットを理解し、リストからトラッカーの削除を自動化することにしました。



ベンコード





これは、.torrentファイルのデータエンコーディング形式と呼ばれるものです。 それは他のどこでもほとんど使用されません。私は、Transmissionに履歴情報を保存するという形式でそれを見つけました。

この形式で作業するためのライブラリはほとんどの現在の言語用に書かれていますが、C ++用ではありません。もちろん、 そういうこともありますが、これは純粋なCであり、プレゼンテーション形式は私には成功しなかったようです。



4つのデータ型-バイト配列、数値、リスト、連想配列が記述されています。



順番に行きましょう:





以上です。 ファイル自体は、このようなエントリのシーケンスです。 したがって、デコードは非常に簡単です。

void CTorrentFile::ReadBencElement(ifstream & fin, tree <BencElement>::pre_order_iterator & parent, string name) { BencElement el; char c = fin.get(); el.name = name; if (c == 'i') { el.type = BencInteger; fin >> el.integer; m_tree.append_child(parent, el); } else if (c == 'l') { int l = fin.peek(); el.type = BencList; tree <BencElement>::pre_order_iterator it = m_tree.append_child(parent, el); while (l != 'e') { ReadBencElement(fin, it, string("")); l = fin.peek(); } fin.seekg(1, ios_base::cur); } else if (c == 'd') { int l = fin.peek(); el.type = BencDict; tree <BencElement>::pre_order_iterator it = m_tree.append_child(parent, el); while (l != 'e') { string name; int len; fin >> len; fin.seekg(1, ios_base::cur); while (len--) { char s = fin.get(); name += s; } ReadBencElement(fin, it, name); l = fin.peek(); } fin.seekg(1, ios_base::cur); } else if (c >= '0' && c <= '9') { fin.seekg(-1, ios_base::cur); int len; el.type = BencString; fin >> len; el.bstr.len = len; // skip ':' fin.seekg(1, ios_base::cur); el.bstr.byteStr = new char[len + 1]; for (int i = 0; i < len; i++) { char s = fin.get(); el.bstr.byteStr[i] = s; } el.bstr.byteStr[el.bstr.len] = 0; m_tree.append_child(parent, el); } }
      
      







コーディングも簡単です。

 void CTorrentFile::WriteBencElement(std::ofstream & fout, tree <BencElement>::sibling_iterator & el) { tree <BencElement>::sibling_iterator it; switch (el->type) { case BencInteger: fout << 'i' << el->integer << 'e'; break; case BencString: fout << el->bstr.len << ':'; fout.write(el->bstr.byteStr, el->bstr.len); break; case BencList: fout << 'l'; it = m_tree.child(el, 0); for (size_t i = 0; i < m_tree.number_of_children(el); i++, ++it) WriteBencElement(fout, it); fout << 'e'; break; case BencDict: fout << 'd'; tree <BencElement>::sibling_iterator it = m_tree.child(el, 0); for (size_t i = 0; i < m_tree.number_of_children(el); i++, ++it) { fout << it->name.length() << ':' << it->name.c_str(); WriteBencElement(fout, it); } fout << 'e'; break; } }
      
      







.torrentファイルの構造。





上で書いたように、Bencodeはコーディングに使用されます。

バイト配列が文字列(連想配列内の要素名、文字列フィールドのみ)として解釈できる場合、utf-8エンコードが使用されることを追加する価値があります。



コンテンツは、次のフィールドを持つ1つの大きな連想配列です。





プロトコルではファイルが断片的に表されていることに言及する必要があります。 つまり、トレントに含まれるファイルは単一の配列に結合され、この配列は比較的小さな断片に分割されました。 この形式では、データはBitTorrentプロトコルによって処理されます。



情報の連想配列は次のもので構成されます。





ファイルリストの要素の形式:





一般に、それですべてです。

現在必要なフィールドは、 announce-listのみです。 このリストを調べて、好ましくないトラッカーを見つけて切り取ります。

 int CTorrentFile::RemoveTracker(const char * mask) { int deletedCount = 0; tree <BencElement>::pre_order_iterator root = m_tree.child(m_tree.begin(), 0); tree <BencElement>::sibling_iterator it = m_tree.child(root, 0); for (size_t i = 0; i < m_tree.number_of_children(root); i++, ++it) { if (it->type == BencString && !it->name.compare("announce") && it->bstr.len > 0 && it->bstr.byteStr) { if (wildcardMatch(it->bstr.byteStr, mask)) { it->bstr.len = 0; it->bstr.byteStr[0] = 0; deletedCount++; } } else if (it->type == BencList && !it->name.compare("announce-list")) { tree <BencElement>::sibling_iterator trackerList = m_tree.child(it, 0); for (size_t j = 0; j < it.number_of_children(); j++) { if (trackerList->type != BencList) { ++trackerList; continue; } tree <BencElement>::sibling_iterator tracker = m_tree.child(trackerList, 0); for (size_t k = 0; k < trackerList.number_of_children(); k++) { if (tracker->type != BencString || tracker->bstr.len <= 0 || !tracker->bstr.byteStr) { ++tracker; continue; } if (wildcardMatch(tracker->bstr.byteStr, mask)) { tracker = m_tree.erase(tracker); deletedCount++; } else ++tracker; } if (trackerList.number_of_children() == 0) trackerList = m_tree.erase(trackerList); else ++trackerList; } } } return deletedCount; }
      
      







すべてを1つのソースに構成します。

ダウンロード -クロスプラットフォーム(win + * nix)、 boost :: filesystemが必要です。



使いやすい:

torrentEditor <file_name> <pattern> 、パターンはワイルドカード文字列( '*'および '?')、私の場合-http://ix*rutracker.net/*

ディレクトリの名前をファイル名に置き換えると、このディレクトリの再帰的な走査と* .torrentファイルの変更が実行されます。

<name> .torrentのバックアップは<name> .oldに保存されます。



デーモンと監視ディレクトリ。





この方法で、既存の.torrentファイルを調べてトラッカーを切り取ることができますが、新しいファイルをどうすればよいでしょうか?

私は便利なものを使用しています-ディレクトリを監視します。 そこで.torrentを投げると、クライアントはこのフォルダーでそれを見つけると、自動的に自分自身に追加します。

ただし、事前にトラッカーを削除したくはありませんが、このビジネスを自動化したいです。

したがって、独自の監視ディレクトリを監視し、トラッカーを削除し、ファイルをtorrentクライアントの監視ディレクトリにスローする単純なデーモンを作成しました。

ユーザーとしての私にとっては、まったく何も変わっていません。ファイルを同じフォルダーに入れて、出力でクライアントのトレントを取得します。



私たちは素晴らしいことを使ってCで悪魔を書きます-inotify

  notifyDesc = inotify_init(); if (notifyDesc < 0) exit(EXIT_FAILURE); watchDesc = inotify_add_watch(notifyDesc, argv[1], IN_CREATE); if (watchDesc < 0) exit(EXIT_FAILURE); // endless loop while (1) { processEvents(notifyDesc, argv[2], argv[3], argv[1]); }
      
      







inotify_init()を使用してモジュールを初期化し、 inotify_add_watch()を追跡するためのディレクトリを追加します。ファイルの作成にのみ関心があるため、 IN_CREATEフラグを指定します。 そして、無限のディレクトリ追跡サイクルをスピンします。

 static void processEvents(int wd, char * moveDir, char * pattern, char * watchDir) { #define BUF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX) * 10) int len, i = 0; char buf[BUF_SIZE]; // blocked read, we wake up when directory changed len = read(wd, buf, BUF_SIZE); while (i < len) { struct inotify_event * ev; ev = (struct inotify_event *)&buf[i]; processNewFile(ev->name, moveDir, pattern, watchDir); i += sizeof(struct inotify_event) + ev->len; } }
      
      





read()のブロック呼び出しは、必要な変更がフォローしているディレクトリの1つで発生するとすぐに制御を返します。 したがって、待機中にプロセッサをロードすることは絶対にありません。

ファイル処理自体はおもしろいものではありません。 名前変更するための呼び出し()システムへの呼び出し()があります。



悪魔も標準です:

  // create child-process pid = fork(); // error? if (pid < 0) exit(EXIT_FAILURE); // parent? if (pid > 0) exit(EXIT_SUCCESS); // new session for child sid = setsid(); if (sid < 0) exit(EXIT_FAILURE); // change current directory if (chdir("/") < 0) exit(EXIT_FAILURE); // close opened descriptors close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO);
      
      







ソースコード



All Articles