Введение
Добрый день.
Я, как и многие, пользуюсь крупным торрент-трекером — rutracker.org, но есть одна особенность, которая меня раздражает. Это добавление адреса в список трекеров ix*.
rutracker.net , который служит неизвестным мне целям.
Однако, что часто (у меня - почти всегда) выдает ошибки( 502 Неверный шлюз И 0 Нет ответа ).
Торрент-клиент (я использую Transmission) помечает торрент как битый.
Что, конечно, меня очень беспокоит. Особенно если учесть функцию «Передача» — она устанавливает статус торрента на основе последнего ответа трекера.
То есть опрашиваем ix*, он возвращает ошибку, торрент помечен как Broken, через n минут/секунд опрашивается следующий трекер из списка - bt*.
rutracker.org или retracker.local , которые возвращают успешный код, и торрент снова становится нормальным.
Меня такая чехарда не особо радует. Решение простое – удалите этот плохой адрес из списка.
Однако файлов у меня много, вырезать их вручную не хочется, да и выполнять дополнительное действие при добавлении нового торрента тоже не хотелось.
Поэтому я решил разобраться в формате и автоматизировать удаление трекера из списка.
Бенкод
Это название формата кодирования данных в файлах .torrent. Он почти нигде больше не используется; Я тоже сталкивался с этим в формате хранения информации о резюме в Transmission. Для большинства современных языков написаны библиотеки для работы с этим форматом, но не для C++, да, конечно, есть такой вещь , но это чистый C и к тому же форма представления мне не показалась удачной, поэтому я написал свой простой велосипед, потому что формат предельно простой.
Описаны четыре типа данных: массив байтов, число, список, ассоциативный массив.
Давайте по порядку:
- Числа даны в виде я е , являются числами в представлении ascii, то есть 1 указывается как «1» или 0x31. Заметно, что таким образом мы можем указывать огромные числа, которые не влезут ни в long, ни в long long, но большинство людей пренебрегают отсутствием ограничения и используют 64-битные числа.
- Байтовый массив - : .
Длина массива также формируется неограниченной последовательностью цифр.
- Список - л е .
Элемент может быть любым из типов данных.
Включая вложенный список.
Конец, как видно из формата, отмечен буквой «е».
- Ассоциативный массив — д е .
Каждый элемент массива выглядит следующим образом: .
Массив байтов — это имя записи в форме из пункта 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. Содержимое представляет собой один большой ассоциативный массив со следующими полями:
- Информация — вложенный ассоциативный массив, собственно описывающий файлы, которые передает торрент.
- объявить — URL трекера.
Вместе с Информация Это обязательное поле, все остальное необязательно.
- список анонсов — список трекеров, если их несколько.
В форме Bencode — список списков.
- Дата создания - дата создания.
Временная метка UNIX.
- комментарий — текстовое описание торрента.
rutracker.org хранит здесь ссылку на тему форума.
- сделано - рассказывает нам, кто создал этот торрент.
То есть файлы, содержащиеся в торренте, объединяются в единый массив, а затем этот массив разбивается на сравнительно небольшие кусочки.
Именно так данные обрабатываются протоколом BitTorrent. Ассоциативный массив Информация включает:
- длина детали - размер одного куска 512 килобайт, 1 метр и так далее.
Слишком большое количество фрагментов приведет к раздуванию файла .
torrent.
- куски — строка, содержащая объединение хешей SHA1, описывающих каждую часть.
Длина этой строки равна 20 * количество штук.
- имя — рекомендательное имя файла (если файл только один) или каталога.
К сожалению, многие торрент-клиенты воспринимают это как аксиому.
- длина — если файл только один, то будет задано поле, в котором указана длина файла.
- файлы — если файлов несколько, появится список ассоциативных массивов.
- длина — длина файла.
- путь — список строк, задающих путь.
Каждая строка представляет собой элемент пути относительно корневого каталога торрента.
По пути а/б/с/d.jpg в этом списке будет 4 строки - ['a', 'b', 'c', 'd.jpg'] .
На данный момент нам нужно только одно поле — список анонсов .
Просматривая этот список, находим ненужный трекер и вырезаем его: 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;
}
Соберем все в один исходник: Скачать — кроссплатформенность (win + *nix), обязательно повышение::файловая система .
Легко использовать: торрент-редактор , где шаблон представляет собой строку подстановочных знаков ('*' и '?'), в моем случае - http://ix*rutracker.net/* Если вы подставите имя каталога вместо имени файла, то будет выполнен рекурсивный обход по этому каталогу и файлы *.
torrent будут изменены.
Резервное копирование для .
торрент сохранено в .
старый .
Демоны и смотреть-каталог.
Таким образом мы можем пройтись по существующим торрент-файлам и вырезать трекер, а как насчет новых файлов? Я пользуюсь удобной вещью - смотреть каталог.
Кидаем туда .
torrent и клиент, найдя его в этой папке, автоматически добавит его к себе.
Однако я вовсе не хочу предварительно резать трекер, а хочу автоматизировать это дело.
Поэтому я написал простой демон, который мониторит свою собственную директорию просмотра, удаляет трекер и кидает файл в директорию просмотра торрент-клиента.
Для меня как пользователя абсолютно ничего не изменилось, я кидаю файлы в ту же папку, а на выходе получаю торрент в клиенте.
Пишем демона на C, используя замечательную штуку — уведомлять , 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;
}
}
Блокировка вызова читать() вернет нам управление, как только нужные нам изменения произойдут в одном из каталогов, которые мы отслеживаем.
Таким образом мы абсолютно не нагружаем процессор во время ожидания.
Сама обработка файлов ничего интересного - пара вызовов переименовать() и один звонок система() .
Демонизация также стандартна: // 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);
Источник .
Теги: #Децентрализованные сети #метаданные #torrent #inotify #rutracker.org #rutracker.org #daemon #bencode
-
Как Создать Легкодоступный Веб-Сайт
19 Oct, 24 -
Valve Объявила Steam Greenlight
19 Oct, 24 -
Os.js: Рабочий Стол Javascript.
19 Oct, 24 -
Как Попасть В Топ Apple App Store
19 Oct, 24