はじめに

こんにちは
私は、多くのように、大きなトレントトラッカー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つのデータ型-バイト配列、数値、リスト、連想配列が記述されています。
順番に行きましょう:
- 番号は、 i <数字のシーケンス> e 、< 数字のシーケンス>の形式で与えられます-これらはASCII表現の数字です。つまり、1は '1'または0x31として指定されます。 この方法で、longまたはlong longのいずれにも適合しない巨大な数値を指定できることは注目に値しますが、ほとんどの場合、制限がないことを無視して64ビットの数値を使用します。
- バイトの配列 - <配列の長さ>:<配列自体> 配列の長さも、無制限の数字列で形成されます。
- リスト-l <リスト項目> e 要素には、任意のデータ型を使用できます。 ネストされたリストを含む。 フォーマットからわかるように、末尾にはリテラル「e」が付いています。
- 連想配列-d <配列要素> e 配列の各要素は次のようになります- <byte array> <element> 。 バイトの配列は、節2の形式のエントリの名前です。要素は、リスト、配列、連想配列、数値など、何でもかまいません。
以上です。 ファイル自体は、このようなエントリのシーケンスです。 したがって、デコードは非常に簡単です。
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つの大きな連想配列です。
- infoは、torrentが転送するファイルを実際に記述するネストされた連想配列です。
- Announce-トラッカーのURL。 情報とともに必須フィールドですが、他のすべてはオプションです。
- アナウンスリスト -トラッカーのリスト(複数ある場合)。 Bencodeビューで、リストのリスト。
- 作成日 -作成日。 UNIXタイムスタンプ。
- comment-トレントのテキスト説明。 rutracker.orgは、フォーラムトピックへのリンクをここに保存します。
- 作成者 -このトレントの作成者を示します。
プロトコルではファイルが断片的に表されていることに言及する必要があります。 つまり、トレントに含まれるファイルは単一の配列に結合され、この配列は比較的小さな断片に分割されました。 この形式では、データはBitTorrentプロトコルによって処理されます。
情報の連想配列は次のもので構成されます。
- ピースの長さ - ピースのサイズは512キロバイト、1メートルなどです。 断片が多すぎると、.torrentファイルが「膨張」します。
- piecesは、各ピースを説明するSHA1ハッシュの連結を含む文字列です。 この文字列の長さは20 *ピースの数です。
- name-推奨されるファイル名(ファイルが1つしかない場合)またはディレクトリ。 残念ながら、多くのトレントクライアントはこれを公理と考えています。
- length-ファイルが1つの場合、このフィールドが設定され、ファイルの長さが含まれます。
- ファイル -複数のファイルがある場合、連想配列のリストが表示されます。
ファイルリストの要素の形式:
- length-ファイルの長さ。
- path-パスを指定する行のリスト。 各行は、トレントのルートディレクトリに相対的なパスの要素です。 パスa / b / c / d.jpgの場合 、このリストには4行あります- ['a'、 'b'、 'c'、 'd.jpg'] 。
一般に、それですべてです。
現在必要なフィールドは、 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);
ソースコード 。