|
|
|
|
|
|
Рассылки Subscribe.ru |
Ограничение числа подключений при скачивании файлов в PHPРазвивая тему закачки файлов через скрипты PHP с ограничением скорости рассмотрим ограничение количества скачиваемых файлов. Для ограничения количества закачиваемых файлов, по какому-либо критерию, нам потребуется организовать обмен данными между двумя копиями одного скрипта, запущенными в пределах сеанса клиента. Это можно организовать через сохранение информации о запуске скрипта в базе данных или файле, установкой «куки» или использованием сессионных переменных. Правда, ограничения привязанные к сессии или cookie вряд ли могут надёжно помочь нам в этом случае. Клиенту достаточно очистить cookies или воспользоваться другим браузером или программой закачки. Единственный реальный на мой взгляд способ должен использовать ограничение количества закачиваемых файлов на основе IP-адреса клиента. Продумаем функционал скрипта для описанной задачи. При запуске он должен выяснить IP-адрес клиента и получить число файлов уже скачиваемых с него. Если это число 0 или не превышает установленного ограничения, то скрипт должен увеличить его на единицу и начать отдачу файла, в противном случае отказать в доступе к файлу. По завершении работы скрипт должен уменьшить число скачиваемых файлов на единицу. Последнее условие очень важно, поскольку этот код должен выполняться даже при разрыве соединения самим клиентом, хотя в этом случае код может быть выполнен не полностью. Для того того чтобы обеспечить его выполнение придется использовать функцию register_shutdown_function(). Поскольку вопросы разных способов отдачи файла мы уже подробно рассмотрели ранее в готовых функциях, нас будет интересовать только их обвязка, ограничивающая число подключений с одного IP-адреса. Итак, пусть наш скрипт закачки находится в файле file.php, а имя файла из того же каталога или из его подкаталога задается в запросе через параметр file. Код скрипта будет таким: // Регистрируем функцию завершения скрипта. Функция должна увеличить на единицу значение // счетчика закачек для данного IP. Конкретная реализация этой функции может быть любой. // При обрыве соединения сервер сразу прекращает работу скрипта, поэтому увеличенное на // единицу значение счетчика закачек не будет уменьшено назад. Только через регистрацию // функции на завершение можно гарантировать ее вызов не только при нормальном завершении // работы, но и при обрыве соединения с клиентской или серверной стороны. register_shutdown_function ("thread_stop"); // Стартуем данный поток. Эта функция должна увеличить на единицу значения счетчика числа // закачек с данного IP. Конкретная реализация этой функции может быть любой. thread_start(); // Задаём количество потоков на одну сессию. Если задано 0, то потоки не ограничиваются $thread_limit = 3; // Функция thread_number() возвращает нам номер текущего потока. Конкретная реализация этой // функции может быть любой. if ($thread_limit > 0 && thread_number() > $thread_limit) { // Если задано ограничение числа потоков и полученный номер текущего потока превышает лимит, // то отдаём клиенту заголовок 503 (Сервис временно недоступен) примерное время ожидания // (240 секунд)и прерываем его выполнение. Можно вместо этого при помощи функции header() // перенаправить его на специальную страницу, где будет описана причина ограничений: // превышение числа одновременно закачиваемых файлов с одного IP-адреса. header($_SERVER["SERVER_PROTOCOL"] . " 503 Service Temporarily Unavailable"); header("Status: 503 Service Temporarily Unavailable"); header("Retry-After: 240"); header("Connection: Close"); exit(); } else { // Отдаем клиенту файл при помощи любой из функций, описанных в предыдущих заметках. Имя // файла для закачки передается через переменную file. file_download($_GET['file']); } Функция file_download() может применяться любая из моих предыдущих заметок. Либо просто передающая файл на закачку напрямую или через скрипт, либо ограничивающая скорость закачки. Ну а реализация функций thread_start(), thread_stop() и thread_number() возможна посредство сохранения данных как в БД, так и в файловой системе сайта. Хотя стоит отметить, что в PHP 5 имеются и другие способы обмена данными между скриптами. Сохранение данных в файловой системеОдним из способов организовать возможность сохранения данных для нескольких запускаемых копий одного PHP-скрипта, являются файлы, уникальные для каждого IP-адреса и каждого потока закачки с него. Разместим в папке нашего скрипта подпапку с названием lock и при подключении каждого IP-адреса будем создавать в ней папки с именем соответствующим этому IP. Для каждого потока закачки файлов мы будем создавать в этих папках файлы нулевого размера. Т.е. если с адреса 192.168.0.1 у нас качается два файла, то в соответствующей папке (lock/192.168.0.1) у нас будут находится два файла 1 и 2. Функция thread_number()// Эта функция возвращает номер текущего потока для данного IP-адреса function thread_number(){ // Задаем имя папки для размещения файлов отмечающих каждый поток // В папке lock каждому IP-адресу будет соответствовать папка с именем равным IP $lock_dir = './lock/' . $_SERVER['REMOTE_ADDR']; // Очищаем кэш файловых операций clearstatcache(); // Если папка не существует (закачка с данного IP еще не производилась), то создаем ее // с правами на полный доступ if(!is_dir($lock_dir)) { mkdir($lock_dir); chmod($lock_dir, 0777); } // Получаем список файлов в папке $files = scandir($lock_dir); // Поскольку даже в пустой папке присутствуют два системных имени: // "." - текущая папка и ".." - родительская папка, то удаляем их из списка array_shift($files); array_shift($files); // Возвращаем количество файлов. Это и будет номер текущего потока запущенного с IP-адреса return count($files); } Функция thread_start()// Эта функция, запускаемая при начале скрипта, будет отмечать каждый поток, для каждого IP-адреса function thread_start(){ // Задаём имя файла для отметки текущего потока. // Он будет на единицу больше числа возвращаемого функцией thread_number() $lock_file = './lock/' . $_SERVER['REMOTE_ADDR'] .'/'. (thread_number()+1); // Эти две функции создадут нам файл нулевого размера. $f = fopen($lock_file, 'w'); fclose($f); // Выставляем права на файл для его беспрепятственного удаления в конце работы скрипта chmod($lock_file, 0777); } Функция thread_stop()// Эта функция удаляет файл отмечающий данный поток для данного IP-адреса или всю папку function thread_stop(){ // Задаем имя папк, где размещены файлы отмечающих каждый поток для текущего IP. $lock_dir = './lock/' . $_SERVER['REMOTE_ADDR']; // Получаем номер текущего скрипта. В силу того, что для любого его запуска создается свой файл // эта величина всегда будет больше единицы $number = thread_number(); if($number==1) { // При завершении единственного скрипта, запущенного с данного IP будут удалены и папка и файл unlink($lock_dir .'/'. $number); rmdir($lock_dir); } elseif($number>1) { // Если кроме нашего скрипты запущены другие его копии с этого же IP, удаляем только файл unlink($lock_dir .'/'. $number); } else{ // Данная ситуация невозможна при корректной работе скрипта, но все равно отследим ее. // Если в папке нет ни одного файла, просто удаляем папку. rmdir($lock_dir); } } Сохранение данных в MySQLВторой доступный способ сохранения данных о количестве закачек – это сохранение информации в базе данных. Для каждого IP-адреса мыбудем создавать отдельную запись со счетчиком потоков. Код при этом оказывается проще, чем в предыдущем случае – мы всего лишь выполняем простые SQL-запросы. Создадим требуемую таблицу: CREATE TABLE threads ( `ip` int(10) unsigned NOT NULL, `count` smallint(5) unsigned NOT NULL, PRIMARY KEY (`ip`) ) Код требуемых нам функций thread_number(), thread_start() и thread_stop() будет следующим. Функция thread_number()// Эта функция возвращает номер текущего потока для данного IP-адреса function thread_number(){ // Выполняем запрос для записи с ключом // соответствующим числовому значению IP-адреса $res = mysql_query("SELECT `count` FROM `threads` WHERE `ip` = ". ip2long($_SERVER['REMOTE_ADDR'])); // Если запись присутствует, // то возвращаем указанное в ней число потоков if ($line = mysql_fetch_array($res, MYSQL_ASSOC)) return (int) $line['count']; // Если записи нет возвращаем ноль return 0; } Функция thread_start()// Эта функция, запускаемая при начале скрипта, будет отмечать каждый поток, для каждого IP-адреса function thread_start(){ // Получаем номер очередного потока if(thread_number() == 0) { // Если потоков с этого IP не зарегистрировано, // то создаём для него запись mysql_query("INSERT INTO `threads` (`ip`, `count`) VALUES ( ". ip2long($_SERVER['REMOTE_ADDR']) .", 1)"); } else { // Если с данного IP уже скачиваются потоки, // увеличиваем счётчик на единицу mysql_query("UPDATE `threads` SET `count` = `count` + 1 WHERE `ip` = ". ip2long($_SERVER['REMOTE_ADDR']); } } Функция thread_stop()// Эта функция отмечает окончание данного потока для данного IP-адреса function thread_stop(){ $number = thread_number(); if($number == 1) { // Если остался один поток, // то запись из таблицы удаляется mysql_query("DELETE FROM `threads` WHERE `ip` = ". ip2long($_SERVER['REMOTE_ADDR'])); } else { // Если все еще есть активные потоки, // то счетчик уменьшается на единицу mysql_query("UPDATE `threads` SET `count` = `count` - 1 WHERE `ip` = ". ip2long($_SERVER['REMOTE_ADDR'])); } } |
|
Direqtor Home Page by ASIADATA. |
|
С помощью APC и сессий делает
С помощью APC и сессий делает в 3 строки
(это код на сервере который отдает файл)
if(intval(apc_fetch($ekey))== 1 && intval($data['premium']) == 0) {
//возвращаем на страничку скачки (в ФО с таймером и все такое)
}else {
apc_delete($ekey);
apc_store($ekey,1,86400);
}
Спасибо, буду смотреть.
Спасибо, буду смотреть.
НО это ограничение на одном сервере, а если несколько? :)
НО это ограничение на одном сервере, а если несколько? :)
Отправить комментарий