Работа с форматом файлов bencode (BitTorrent) в PHP 19.06.2010
Данную статью я решил разбить на три части:
- Разбор формата bencode (на примере торрент файлов)
- Создание файлов в формате bencode
- Создание torrent файлов (включая хеширование)
Разбор формата bencode
В качестве подопытного файла в формате bencode я взял этот . Но это не существенно, на самом деле подойдет любой другой. Цель, которую я хотел достичь, это получение на выходе массива структурированных данных. Итак, перейдем к нудной теории.
Формат bencode содержит четыре структурных единицы:
- Строка (string).
- Целочисленное число (int)
- Словарь (dictionary), фактически ассоциативный массив
- Список (list)
Теперь немного о том, как они записываются:
Строка
в начале записывается длинна строки в десятичной форме, затем идет двоеточие «:», а затем сама строка. Для примера возьмем слово «Bender», в формате bencode, он записывается как
«6:Bender»
Число
в начале идет символ «i», затем число в десятичной форме (ему не должны предшествовать нули, кроме случая, когда число равно нулю), и в конец дописывается символ «e». На примере числа 42 получаем: «i42e». Запись «i042e» считается неверной, а запись «i0e» верной.
Словарь
началом описания словаря считается символ «d». Далее идет произвольное количество пар ключ-значение, и в конец ставиться символ «e». Ключом может являться только строка (в формате bencode), значением любая структурная единица. Пример словаря, на основе такого массива:
-
-
(
-
"name" => "Bender",
-
"number" => 42
-
)
-
d4:name6:Bender6:numberi42ee
Выглядит немного непонятно, так что добавим пару пробелов и переносов строк для читаемости:
d
4:name 6:Bender
6:number i42e
e
Список
Список схож со словарем, с той лишь разницей, что в списке не используются ключи (неассоциативный массив). Начинается список с символа «i», содержит произвольное количество подряд идущих элементов (любых структурных единиц bencode), и заканчивается символом «e». Пример списка содержащего строку «Bender» и число «42»: l6:Benderi42ee, или если записать с пробелами:
l
6:Bender
i42e
e
Теперь перейдем к написанию парсера, на вход которому подается строка, а на выход получаем PHP массив. Немного поразмыслив, я пришел к такой структуре класса декодера:
-
-
class Bencode_Decode
-
{
-
protected
-
$_data = "", // Строка, подвергаемая парсингу
-
$_position = 0, // Указатель на текущую позицию парсера
-
$_length = 0; // Длина данных
-
-
public function decodeString($data); // Декодирует строку
-
public function decodeFile($file); //Открывает файл и отдает его содержимое на расшифроку
-
-
protected function detect(); // Определяет, тип следующей структуры
-
protected function getDictionary(); // Возвращает данные словаря
-
protected function getList(); // Возвращает список
-
protected function getString(); // Возвращает строку
-
protected function getInt(); // Возвращает число
-
-
protected function getChar(); // Возвращает символ, на который ссылается указатель
-
protected function (); //Перемещает указатель на следующий символ
-
}
-
-
Теперь немного подробнее. Дабы не приводить полный листинг программы, предлагаю его вам скачать здесь
decodeString()
записывает данные в _data, сбрасывает указатель в 0 и запускает детектор
detect()
Определяет тип данных и передает соответствующему обработчику. Если тип не может быть определен — выкидывает Exception
getString()
Считываем данные до символа «:», это будет длинна строки. Теперь мы знаем длину и без проблем можем получить строку. Незабываем перенести указатель на символ после конца строки.
getInt()
Считываем данные от «i» до «e», это и есть наше число. Переносим указатель на символ после «e».
getDictionary()
Здесь все несколько сложнее. Будем считывать пары ключ-значение, до того момента пока следующим извлеченным символом не окажется «e». Извлечение ключа сводится к вызову функции getString(), а получение значения к вызову detect(). Смещаем указатель на символ после «e»
getList()
эта функция работает также как и getDictionary, только без извлечения ключа.
Испытания
Для проверки работоспособности написанного класса, натравливаем его на торрент файл из начала статьи.
$bencode = new Bencode_Decode();
var_dump($bencode -> decodeFile('Big_Buck_Bunny_1080p_surround_frostclick.com_frostwire.com.torrent'));
у меня получился вот такой вот результат (для сохранения форматирования, прикладываю его в виде изображения):

В следующей статье я продемонстрирую, как с помощью php можно преобразовать массив (такой как на изображении) в строку формата bencode
Похожие статьи
- Паттерн проектирования Прокси (Proxy) на PHP
- Вышел PHP 5.4a1
- Как закрыть соединение и продолжить выполнение скрипта
- Встроенный Web сервер в PHP
- Вышел NetBeans 7 Beta 2
