Vortex777
Интересующийся
- Регистрация
- 27.06.2022
- Сообщения
- 98
- Реакции
- 0
- Гарант продажи
- 0
- Гарант покупки
- 0
- Депозит
- 0 р
Введение.
Данный материал рассматривает процесс создания программного обеспечения, реализующего распределенные вычисления. Будучи рассчитанным на невысокий уровень подготовки читателя, он не претендует на открытие чего-либо нового. Автор попытался поэтапно описать процесс создания программы с наиболее детальными комментариями к коду и объяснить значение каждой команды в листингах. Гуру программирования получат отличную площадку для критики и возможность предложить свои идеи, касающиеся оптимизации кода.
О виновнике торжества.
Точнее - о виновниках. Распределенные вычисления - способ решения трудоёмких вычислительных задач с использованием двух или более компьютеров, объединённых в сеть. В свою очередь, эта сеть компьютеров называется ботнетом (более подробную информацию можно получить, перейдя по ссылке в конце статьи).
В настоящее время под «ботнетом» в большинстве случаев понимается сеть компьютеров, зараженных вредоносным программным обеспечением – ботом. Однако, принцип любого ботнета – это прежде всего какие-либо распределенные действия, поэтому автор материала предлагает читателям абстрагироваться от понятия «ботнет» и приступить к разработке необходимого для организации распределенных вычислений ПО.
Шаг первый.
С первого шага начинается путешествие в тысячу ли (с) (Лао-Цзы). Нашим отправным пунктом станет проектирование программы (чтобы не повторяться, назовем ее «ботом»).
Бот должен уметь:
1. получать команду от сервера;
2. обрабатывать команду, т. е. классифицировать ее на «известную» или «неизвестную» и соответствующим образом обрабатывать ее параметры;
3. выполнять команду.
За кажущейся примитивностью наших требований скрывается много подводных камней. Например, как бот будет узнавать о новых командах? Для этого появляется еще одно требование: программа должна поддерживать плагины. На плюсах плагинной технологии заострять внимание нет смысла, т. к. очевидно, что расширяемость программы и ее модернизация в нашем случае окажется полезной. Теперь перейдем к практической части, разбавленной теоретическими выкладками.
Шаг второй. Создаем соединение и получаем команды.
При написании программы использовалась среда программирования MS Visual Studio 2008. Чтобы не заниматься рутиной (имеется в виду работа с сокетами), воспользуемся стандартной библиотекой, которая присутствует в Windows: wininet.dll. Данная DLL представляет собой API для доступа к общим протоколам интернет, включая FTP, HTTP и Gopher. Это высокоуровневый API, позволяющий, в отличие от WinSock или TCP/IP, не заботиться о деталях реализации соответствующих интернет протоколов. Кстати, именно ее использует MS Internet Explorer.
Если с инструментами все понятно, то детали, касающиеся алгоритма работы программы, требуют пояснения. Пусть команды, предназначенные боту, находятся на сервере в файле command.txt. Тогда для получения команд бот должен периодически соединяться с сервером и скачивать указанный файл. Следующий листинг демонстрирует описанные действия:
Шаг третий. Обрабатываем командный файл и подключаем необходимые DLL.
Под обработкой командного файла автор подразумевает действие программы, в результате которого бот получает две строки: название команды и строка, содержащая параметры этой команды (перечисленные через символ пробела). Командный файл имеет следующую структуру команды:
<команда(1)> [параметр(1)] [параметр(2)] … [параметр(i)]
<команда(2)> [параметр(1)] [параметр(2)] … [параметр(j)]
…
<команда(k)> [параметр(1)] [параметр(2)] … [параметр]
где i, j, k меняются в интервале (1; бесконечность).
Действия бота следующие:
1. выделение k-ой строки;
2. передача выделенной строки в функцию, которая реализует подключение библиотеки, необходимой для выполнения команды (в листинге – функция PlugLibrary);
3. PlugLibrary сама разделяет строку на команду и параметры и выполняет необходимое действие, зависящие от типа команды.
Первые 2 пункта реализованы в следующей функции:
Функция PlugLibrary отделяет команду от параметров. Далее, в зависимости от типа команды, функция выполняет одно из следующих действий:
1. Удаление известной команды (фактически: удаление библиотеки, в которой реализована команда).
2. Загрузка файла. Если файл является DLL, то подключение этой библиотеки; если является исполняемым файлом или т.п., то запуск этого файла.
3. Если известная команда, то подключение ее библиотеки и запуск.
Код рассмотренной функции приведен ниже.
Данный материал рассматривает процесс создания программного обеспечения, реализующего распределенные вычисления. Будучи рассчитанным на невысокий уровень подготовки читателя, он не претендует на открытие чего-либо нового. Автор попытался поэтапно описать процесс создания программы с наиболее детальными комментариями к коду и объяснить значение каждой команды в листингах. Гуру программирования получат отличную площадку для критики и возможность предложить свои идеи, касающиеся оптимизации кода.
О виновнике торжества.
Точнее - о виновниках. Распределенные вычисления - способ решения трудоёмких вычислительных задач с использованием двух или более компьютеров, объединённых в сеть. В свою очередь, эта сеть компьютеров называется ботнетом (более подробную информацию можно получить, перейдя по ссылке в конце статьи).
В настоящее время под «ботнетом» в большинстве случаев понимается сеть компьютеров, зараженных вредоносным программным обеспечением – ботом. Однако, принцип любого ботнета – это прежде всего какие-либо распределенные действия, поэтому автор материала предлагает читателям абстрагироваться от понятия «ботнет» и приступить к разработке необходимого для организации распределенных вычислений ПО.
Шаг первый.
С первого шага начинается путешествие в тысячу ли (с) (Лао-Цзы). Нашим отправным пунктом станет проектирование программы (чтобы не повторяться, назовем ее «ботом»).
Бот должен уметь:
1. получать команду от сервера;
2. обрабатывать команду, т. е. классифицировать ее на «известную» или «неизвестную» и соответствующим образом обрабатывать ее параметры;
3. выполнять команду.
За кажущейся примитивностью наших требований скрывается много подводных камней. Например, как бот будет узнавать о новых командах? Для этого появляется еще одно требование: программа должна поддерживать плагины. На плюсах плагинной технологии заострять внимание нет смысла, т. к. очевидно, что расширяемость программы и ее модернизация в нашем случае окажется полезной. Теперь перейдем к практической части, разбавленной теоретическими выкладками.
Шаг второй. Создаем соединение и получаем команды.
При написании программы использовалась среда программирования MS Visual Studio 2008. Чтобы не заниматься рутиной (имеется в виду работа с сокетами), воспользуемся стандартной библиотекой, которая присутствует в Windows: wininet.dll. Данная DLL представляет собой API для доступа к общим протоколам интернет, включая FTP, HTTP и Gopher. Это высокоуровневый API, позволяющий, в отличие от WinSock или TCP/IP, не заботиться о деталях реализации соответствующих интернет протоколов. Кстати, именно ее использует MS Internet Explorer.
Если с инструментами все понятно, то детали, касающиеся алгоритма работы программы, требуют пояснения. Пусть команды, предназначенные боту, находятся на сервере в файле command.txt. Тогда для получения команд бот должен периодически соединяться с сервером и скачивать указанный файл. Следующий листинг демонстрирует описанные действия:
Код:
//httpd.cpp
//Реализация скачивания файла
#include <windows.h>
#include <wininet.h>
#pragma comment(lib,"wininet")//подключаем библиотеку WinInet
void HTTPDownload(char *FileUrl, char *FileName)//передаются параметры: путь к файлу на сервере, имя файла для сохранения результата запроса
{
BYTE bBuffer[4096];
DWORD dCount=0;
HANDLE hOutputFile;
hOutputFile = CreateFile(//создание файла, в который сохраняются результаты запроса
FileName,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_NEW, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
HINTERNET hInternet = InternetOpen( //инициализация библиотеки WinInet.dll (получение дескриптора соединения)
"DLLDownloader",//параметр, который определяет информацию в поле "User Agent"
INTERNET_OPEN_TYPE_PRECONFIG,//тип доступа (прямой или прокси); параметр по умолчанию (настройки из реестра)
NULL,
NULL,
0);
if(hInternet !=NULL)
{
HINTERNET hRequest = InternetOpenUrl(//создание и отправка запроса серверу (получение дескриптора сессии)
hInternet,//дескриптор соединения
FileUrl,//путь к скачиваемому файлу на сервере
0, 0, 0, 0);
if(hRequest != NULL)
{
do {
BOOL bSend = InternetReadFile(//чтение результата запроса
hRequest,//дескриптор сессии
bBuffer,//адрес буфера, содержащего скачиваемые данные
sizeof(bBuffer),//размер буфера
&dCount);//количество скачиваемых байт
WriteFile(hOutputFile, bBuffer, dCount, &dCount, 0);//запись результата запроса в файл
}
while (dCount == 4096);
}
InternetCloseHandle(hRequest);//закрытие запроса
}
InternetCloseHandle(hInternet);//закрытие соединения
CloseHandle(hOutputFile);//закрытие файла
}
Под обработкой командного файла автор подразумевает действие программы, в результате которого бот получает две строки: название команды и строка, содержащая параметры этой команды (перечисленные через символ пробела). Командный файл имеет следующую структуру команды:
<команда(1)> [параметр(1)] [параметр(2)] … [параметр(i)]
<команда(2)> [параметр(1)] [параметр(2)] … [параметр(j)]
…
<команда(k)> [параметр(1)] [параметр(2)] … [параметр]
где i, j, k меняются в интервале (1; бесконечность).
Действия бота следующие:
1. выделение k-ой строки;
2. передача выделенной строки в функцию, которая реализует подключение библиотеки, необходимой для выполнения команды (в листинге – функция PlugLibrary);
3. PlugLibrary сама разделяет строку на команду и параметры и выполняет необходимое действие, зависящие от типа команды.
Первые 2 пункта реализованы в следующей функции:
Код:
//parse.cpp
//Реализация обработчика (парсера) командных файлов
#include <windows.h>
#include "pluglib.h"//подключение заголовочного файла, содержащего прототип функции "PlugLibrary"
void Parse(char *FileName)//передача имени командного файла
{
DWORD dwCount=0;
char cCurrentSymbol;
int iCount=0;
char Command[256];//максимальный рамер строки в командном файле
HANDLE hFile=CreateFile(//открытие командного файла
FileName,
GENERIC_READ,//открытие в режиме чтения
FILE_SHARE_READ,//разрешение доступа к файлу для нескольких процессов
NULL,
OPEN_EXISTING,//открытие существующего файла
0, NULL);
//чтение содержимого командного файла
while (true)
{
ReadFile(//чтение данных
hFile,//дескриптор файла, полученный при открытии
&cCurrentSymbol,//адрес буфера для считываемого символа
1,//размер буфера для считываемого символа
&dwCount,//количество прочитанных байт
NULL);
if (dwCount==0) break;
if (cCurrentSymbol=='\n')//обнаружение перехода на следующую строку
{
Command[iCount]='\0';//создание строки
PlugLibrary(Command);//передача строки для выполнения
iCount=0;
}
else
{
Command[iCount]=cCurrentSymbol;//добавление прочитанного символа к массиву символов
iCount++;
}
}
CloseHandle(hFile);//закрытие файла
}
1. Удаление известной команды (фактически: удаление библиотеки, в которой реализована команда).
2. Загрузка файла. Если файл является DLL, то подключение этой библиотеки; если является исполняемым файлом или т.п., то запуск этого файла.
3. Если известная команда, то подключение ее библиотеки и запуск.
Код рассмотренной функции приведен ниже.