Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МетУказКурсРабСистемПрогрЗИЭИТ2005.doc
Скачиваний:
6
Добавлен:
13.09.2019
Размер:
698.37 Кб
Скачать

Тема 19. Ftp - сервер.

Техническое задание.

Программный продукт должен быть реализован с использованием WinSock API, соединение устанавливается на портах 20 и 21 протокола ТСР. Среда разработки VC++. Реализации подлежат основные команды протокола FTP, которые дадут возможность взаимодействия с одной из стандартных программ FTP-клиент.

Методические указания.

Ориентировочный алгоритм работы сервера FTP

1. Создает гкездо прослушивания и связывает его с портом 21 TCP (FTP по умолчанию).

2. Переводит гнездо в режим прослушивания в ожидании поступающих запросов на соединение.

3. После получения запроса на соединение принимает соединение и создает новое гнездо.

4. Если сервер уже имеет открытое соединение гнезда, отправляет сообщение об отказе новому соединению и затем сразу же закрывает соединение.

5. Если у сервера еще нет активного соединения, он отправляет приветствие клиенту. После отправления ответа клиенту сервер переходит в режим ожидания до- тех пор, пока клиент не сделает следующий запрос.

6. Возвращает сообщение ответа. Если сервер получает команду USER, он возвращает сообщение ответа об успехе. Если сервер получает команду PASS, он отправляет сообщение с положительным ответом.

7. Ожидает, пока будет принята команда от клиента. Командами, которые принимает сервер, являются PORT, TYPE, MODE, RETR, STOR, PWD, XPWD, LIST, NLST, QUIT. На все другие команды сервер отвечает сообщением об ошибке.

8. Получив команды LIST, NLST, STOR или RETR, создает гнездо данных, подсоединяет его к клиенту, а затем запускает передачу данных.

9. После завершения передачи данных закрывает соединение данных.

10. Получив команду QUIT от клиента, отправляет прощальное сообщение и закрывает соединение.

Работа с сокетами на уровне WinSock API описана в Приложении 1.

Коммуникационная модель FTP описана в документе RFC 959.

Основные команды FTP приводятся в Приложении 2.

Тема 20. Системные сервисы Windows nt/2000.

Техническое задание.

Предложить и реализовать новый системный сервис.

Методические указания.

Любая ОС нуждается в средствах для запуска фоновых задач, т.е. таких, которые постоянно работают в памяти, не обращая внимания, кто использует машину. Эти фоновые приложения могут выполнять различные действия в системе. К примеру, система обработки сообщений может наблюдать за сетью и выводить диалоговое окно всякий раз при получении сообщения от другой машины. Приложению, которое посылает и получает факсы, необходимо стартовать при загрузке ОС и продолжать отслеживать поступление новых факсов во все время работы компьютера. Всем этим задачам необходимо время CPU для выполнения своей работы, но они должны это делать, не отвлекая пользователя, т.к. все эти задачи требуют небольшого времени работы CPU.

В MS-DOS такие фоновые программы назывались TSR (Terminate and Stay Resident). Они запускались в autoexec.bat. В UNIX фоновые программы называются "демонами". В Windows NT фоновые программы называются сервисами. Сервисы стартуют автоматически, когда NT загружается, и остаются запущенными в фоновом режиме, не обращая внимания, кто, и с какими правами вошел в систему.

Сервисы Windows NT выполняются как обычные exe файлы, но они должны поддерживать специфичный внутренний протокол, позволяющий им корректно взаимодействовать с SMC (Service Control Manager).

Основные понятия

Сервисы бывают двух видов. Сервисы драйверов устройств используют специальный протокол для взаимодействия с железом. Сервисы Win32, с другой стороны, выполняют большинство фоновых задач, используя вызовы Win32 API. В данной курсовой работе предлагается рассмотреть именно эти сервисы. Любой программист с NT SDK (или Visual C++) и правами доступа администратора к машине с NT может разработать и установить свой собственный сервис. В общем, если Вам необходимо написать какую-либо программу, которая стартует при запуске NT, работает в фоновом режиме, то Вам необходимо создать сервис Win32.

Сервисы видны пользователю в панели управления. Запустив Service Applet, вы увидите список доступных сервисов. Вы можете запускать, останавливать, ставить сервисы на паузу. Нажав кнопку Startup, Вы можете изменить поведение сервиса при запуске. Сервисы могут запускаться автоматически при старте компьютера, быть абсолютно выключеными или иметь возможность запуска вручную. Если сервис запускается вручную, то пользователь может указать параметры запуска. Вам нужно иметь права доступа администратора или права power user, чтобы что-либо делать с сервисами.

Windows NT загружает некоторое количество предустановленных сервисов, таких как network messaging, command shedulling и RPC naming. После создания своего собственного сервиса Вы должны выполнить отдельно инсталляцию, чтобы включить его в список. В процессе установки в реестр добавляется информация о новом сервисе - его имя, имя исполняемого файла и т.п. Это позволит SCM узнать о новом сервисе при последующей перезагрузке.

Создание нового сервиса

Как уже было сказано, сервис - это обычный выполняемый файл, но поддерживающий интерфейс с SCM. Microsoft очень аккуратно следит за ходом вызова функций, и Вы обязаны тщательно следовать плану по разработке сервисов, предложенному Microsoft. В ином случае сервис работать не будет. Более подробную информацию Вы найдете в хэлпе.

  • В коде сервиса обязательно должны быть или main, или WinMain. В ходе выполнения main должна немедленно вызвать StartServiceCrtlDispatcher. Вызывая эту функцию, Вы инициализируйте указатель на функцию ServiceMain. Этот указатель будет использован SCM для запуска сервиса.

  • SCM вызывает ServiceMain когда, к примеру, администратор нажимает кнопку Start в ServiceApplet. Выполнение ServiceMain происходит в отдельном потоке. ServiceMain должна вызвать RegisterServiceCtrlHandler, которая, в свою очередь, регистрирует ф-ию Handle для SCM. Handle обрабатывает запросы SCM, о них будет сказано ниже. Вы можете присвоить данной ф-ий любое имя, не обязательно Handle, просто в документации она названа Handle. Ф-ия RegisterServiceCtrlHandler возвращает хэндл, который сервис может использовать, когда нужно послать сообщение SCM.

  • Ф-ия ServiceMain обязана также создать и запустить поток, выполняющий собственно код сервиса. Функция ServiceMain не должна заверщать свою работу, пока сервис работает. Когда ServiceMain завершает работу, сервис останавливается.

  • Функция Handle пытается определить, какой из запросов был получен от SCM.

    • SERVICE_CONTROL_STOP - Говорит сервису, что надо остановиться.

    • SERVICE_CONTROL_PAUSE - Сервис должен стать на паузу.

    • SERVICE_CONTROL_CONTINUE - Говорит сервису, что надо продолжить работу.

    • SERVICE_CONTROL_INTERROGATE - Говорит сервису, что тому следует немедленно сообщить свой статус.

    • SERVICE_CONTROL_SHUTDOWN - Говорит сервису, что скоро будет выключение компьютера .

Также возможно создание собственных констант (со значениями от 128 до 255) и посылка их сервису от SCM.

После того, как Вы создали exe файл, который содержит функции main, ServiceMain, Handler, также как и функцию, содержащую поток сервиса, считайте, что сервис создан. На рисунке показано отношение SCM и перечисленных функций.

   В листинге 1 показан простейший из сервисов. Он просто издает звуки каждые две секунды. Вы можете модифицировать этот интервал, задавая предстартовые параметры. Это полностью законченный сервис, реагирующий на все команды от SCM. По этой причине Вы можете использовать его как шаблон для Ваших сервисов. Ф-ия main вызывает StartServiceCrtlDispatcher для регистрации ф-ии ServiceMain. Эта регистрация выполняется с использованием структуры SERVICE_TABLE_ENTRY. В нашем случае программа содержит только один сервис, поэтому есть только одна точка входа в таблицу. Однако возможно создание нескольких сервисов в одном exe. В этом случае будет идентифицирована соответствуюшая ServiceMain для каждого сервиса. Также возможно следующее: поместить некоторый код инициализации в ф-ию main до вызова StartServiceCrtlDispatcher, но этот код должен быть выполнен не более чем за 30 секунд, иначе SCM прервет выполнение работы сервиса, предполагая, что, что-то не так. Ф-ия ServiceMain вызывается из SCM когда сервис стартует при загрузке ОС, либо при ручном старте. ServiceMain всегда выполняет следующие шаги:

  1. Немедленный вызов RegisterServiceCtrHandler для регистрации ф-ии Handler в SCM как ф-ии хэндлер процесса.

  2. Затем вызывается SendStatsToSCM. Это оповещает SCM о прогрессе в инициализации сервиса. Четвертый параметр - это значение, которое увеличивается каждый раз, когда программа обновляет статус. SCM и другие программы могут видеть это значение и следить за ходом установки. Последний параметр сообщает SCM как долго (в м.сек.) SCM должен ждать, прежде чем увеличить третий параметр.

  3. Далее генерируется событие, которое может быть использовано в конце ф-ии для предотвращения выхода из нее. Прежде чем это случится, SCM должен выйти по запросу STOP.

  4. Следующим шагом ServiceMain проверяет наличие стартовых параметров, которые пользователь мог задать в Startup Parameters. Параметры обрабатываются в ServiceMain как массив.

  5. Если Вашему сервису требуется инициализация каких-либо задач, то это должно происходить до вызова ф-ии InitService.

  6. Далее стартует InitService, которая запускает трэд с реальной работой сервиса. Если этот вызов происходит успешно, то ServiceMain уведомляет SCM о том, что сервис стартовал удачно.

  7. Запускается функция WaitForSingleObject, которая ждет от Handler появления события terminateEvent. После чего работа функции ServiceMain заканчивается, сервис останавливается.

Как видите, эта функция не слишком гибкая. Исключая шаг №5, Вы обязаны делать все именно так, а не иначе. Вызов в конце функции ServiceMain terminate закрывает все открытые хэндлы и посылает сообщение SCM о том, что сервис остановлен.

SCM вызывает Handler всякий раз, когда нужно поставить на паузу, возобновить выполнение или остановить сервис. Для остановки сервиса Handler устанавливает событие terminateEvent. Делая так, он говорит ServiceMain, запущенной в отдельном трэде, что следует остановиться и закончить свою работу. Прекратив работу ServiceMain Вы останавливаете и сервис. Действие ф-ии SendStatusToSCM заключается в передаче статуса действия сервиса SCM.  В приведенном примере InitService создает поток сервиса, в нашем случае бесконечный цикл, который "бикает" и "засыпает" на заданные интервалы. Естественно, в своих сервисах Вы поставите здесь свой, более умный код.

Установка и удаление сервиса

Чтобы использовать описанный выше сервис, его необходимо инсталлировать. Инсталляция позволит SCM узнать о сервисе и позволит добавть сервис в общий список сервисов в Service Applet. В листинге №2 показано как это сделать.

Вначале вызывается OpenSCManager, которая устанавливает связь с SCM. В вызове данной ф-ии Вы обязаны указать, что Вы хотите сделать Если у Вас недостаточно полномочий, то SCM вернет Вам NULL.

Вызов CreateService инсталлирует сервис. В качестве параметра используется указатель, возвращенный OpenSCManager, имя, метка для SCM и путь к exe файлу сериса. Использование SERVICE_WIN32_OWN_PROCESS говорит о том, что в exe файле только 1 сервис, а SERVICE_DEMAND_START говорит о том, что данный сервис предпочтительно запускать ручками, а не автоматом. Показано типичное приглашеие для запуска программы установки сервиса:

install BeepService "Beeper" c:\winnt\beep.exe

Первый параметр - имя, используемое внутри SCM. Это же имя используется для удаления сервиса. Второй параметр - метка, видимая в Service Applet. Третий - полный путь к exe файлу сервиса. После установки сервиса запустите его в Service Applet. Если при инсталляции возникли ошибки - смотрите в хэлпе.

В листинге 3 показан процесс удаления сервиса. Открывается соединение с SCM, устанавливается соединение с сервисом посредством OpenService, запрашивается у Handler текущий статус, и, если сервис не остановлен, то останавливает его. Функция DeleteService убирает сервис из Service Applet. Показано типичное приглашеие для запуска программы удаления сервиса:

remove BeepService

Код, реализующий работу простейшего сервиса.

//***************************************************************

// From the book "Win32 System Services: The Heart of Windows NT"

//by Marshall Brain Published by Prentice Hall

// This code implements the simplest possible service.

// It beeps every 2 seconds, or at a user specified interval.

//***************************************************************

// beepserv.cpp

#include <windows.h>

#include <stdio.h>

#include <iostream.h>

#include <stdlib.h>

#define DEFAULT_BEEP_DELAY 2000

// Global variables

// The name of the service

char *SERVICE_NAME = "BeepService";

// Event used to hold ServiceMain from completing

HANDLE terminateEvent = NULL;

// Handle used to communicate status info with

// the SCM. Created by RegisterServiceCtrlHandler

SERVICE_STATUS_HANDLE serviceStatusHandle;

// The beep interval in ms.

int beepDelay = DEFAULT_BEEP_DELAY;

// Flags holding current state of service

BOOL pauseService = FALSE;

BOOL runningService = FALSE;

// Thread for the actual work

HANDLE threadHandle = 0;

void ErrorHandler(char *s, DWORD err)

{

cout << s << endl;

cout << "Error number: " << err << endl;

ExitProcess(err);

}

// This function consolidates the activities of

// updating the service status with

// SetServiceStatus

BOOL SendStatusToSCM (DWORD dwCurrentState,

DWORD dwWin32ExitCode,

DWORD dwServiceSpecificExitCode,

DWORD dwCheckPoint,

DWORD dwWaitHint)

{

BOOL success;

SERVICE_STATUS serviceStatus;

// Fill in all of the SERVICE_STATUS fields

serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;

serviceStatus.dwCurrentState = dwCurrentState;

// If in the process of doing something, then accept

// no control events, else accept anything

if (dwCurrentState == SERVICE_START_PENDING)

serviceStatus.dwControlsAccepted = 0;

else

serviceStatus.dwControlsAccepted =

SERVICE_ACCEPT_STOP |

SERVICE_ACCEPT_PAUSE_CONTINUE |

SERVICE_ACCEPT_SHUTDOWN;

// if a specific exit code is defined, set up

// the win32 exit code properly

if (dwServiceSpecificExitCode == 0)

serviceStatus.dwWin32ExitCode = dwWin32ExitCode;

else

serviceStatus.dwWin32ExitCode =

ERROR_SERVICE_SPECIFIC_ERROR;

serviceStatus.dwServiceSpecificExitCode =

dwServiceSpecificExitCode;

serviceStatus.dwCheckPoint = dwCheckPoint;

serviceStatus.dwWaitHint = dwWaitHint;

// Pass the status record to the SCM

success = SetServiceStatus (serviceStatusHandle,

&serviceStatus);

return success;

}

DWORD ServiceThread(LPDWORD param)

{

while (1)

{

Beep(200,200);

Sleep(beepDelay);

}

return 0;

}

// Initializes the service by starting its thread

BOOL InitService()

{

DWORD id;

// Start the service's thread

threadHandle = CreateThread(0, 0,

(LPTHREAD_START_ROUTINE) ServiceThread,

0, 0, &id);

if (threadHandle==0)

return FALSE;

else

{

runningService = TRUE;

return TRUE;

}

}

// Dispatches events received from the SCM

VOID Handler (DWORD controlCode)

{

DWORD currentState = 0;

BOOL success;

switch(controlCode)

{

// There is no START option because

// ServiceMain gets called on a start

// Stop the service

case SERVICE_CONTROL_STOP:

// Tell the SCM what's happening

success = SendStatusToSCM(SERVICE_STOP_PENDING,

NO_ERROR, 0, 1, 5000);

runningService=FALSE;

// Set the event that is holding ServiceMain

// so that ServiceMain can return

SetEvent(terminateEvent);

return;

// Pause the service

case SERVICE_CONTROL_PAUSE:

if (runningService && !pauseService)

{

// Tell the SCM what's happening

success = SendStatusToSCM(

SERVICE_PAUSE_PENDING,

NO_ERROR, 0, 1, 1000);

pauseService = TRUE;

SuspendThread(threadHandle);

currentState = SERVICE_PAUSED;

}

break;

// Resume from a pause

case SERVICE_CONTROL_CONTINUE:

if (runningService && pauseService)

{

// Tell the SCM what's happening

success = SendStatusToSCM(

SERVICE_CONTINUE_PENDING,

NO_ERROR, 0, 1, 1000);

pauseService=FALSE;

ResumeThread(threadHandle);

currentState = SERVICE_RUNNING;

}

break;

// Update current status

case SERVICE_CONTROL_INTERROGATE:

// it will fall to bottom and send status

break;

// Do nothing in a shutdown. Could do cleanup

// here but it must be very quick.

case SERVICE_CONTROL_SHUTDOWN:

return;

default:

break;

}

SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0);

}

// Handle an error from ServiceMain by cleaning up

// and telling SCM that the service didn't start.

VOID terminate(DWORD error)

{

// if terminateEvent has been created, close it.

if (terminateEvent) CloseHandle(terminateEvent);

// Send a message to the scm to tell about stopage

if (serviceStatusHandle)

SendStatusToSCM(SERVICE_STOPPED, error,

0, 0, 0);

// If the thread has started, kill it off

if (threadHandle) CloseHandle(threadHandle);

// Do not need to close serviceStatusHandle

}

// ServiceMain is called when the SCM wants to start the service. When it returns,

//the service has stopped. It therefore waits on an event just before the end of the //function, and that event gets set when it is time to stop. It also returns on any error //because the service cannot start if there is an eror.

VOID ServiceMain(DWORD argc, LPTSTR *argv)

{

BOOL success;

// immediately call Registration function

serviceStatusHandle =

RegisterServiceCtrlHandler(

SERVICE_NAME, (LPHANDLER_FUNCTION)Handler);

if (!serviceStatusHandle)

{terminate(GetLastError()); return;}

// Notify SCM of progress

success = SendStatusToSCM(SERVICE_START_PENDING,

NO_ERROR, 0, 1, 5000);

if (!success) {terminate(GetLastError()); return;}

// create the termination event

terminateEvent = CreateEvent (0, TRUE, FALSE, 0);

if (!terminateEvent) {terminate(GetLastError()); return;}

// Notify SCM of progress

success = SendStatusToSCM(SERVICE_START_PENDING,

NO_ERROR, 0, 2, 1000);

if (!success) {terminate(GetLastError()); return;}

// Check for startup params

if (argc == 2)

{

int temp = atoi(argv[1]);

if (temp < 1000)

beepDelay = DEFAULT_BEEP_DELAY;

else

beepDelay = temp;

}

// Notify SCM of progress

success = SendStatusToSCM(SERVICE_START_PENDING,

NO_ERROR, 0, 3, 5000);

if (!success) {terminate(GetLastError()); return;}

// Start the service itself

success = InitService();

if (!success) {terminate(GetLastError()); return;}

// The service is now running.

// Notify SCM of progress

success = SendStatusToSCM(SERVICE_RUNNING,

NO_ERROR, 0, 0, 0);

if (!success) {terminate(GetLastError()); return;}

// Wait for stop signal, and then terminate

WaitForSingleObject (terminateEvent, INFINITE);

terminate(0);

}

VOID main(VOID)

{

SERVICE_TABLE_ENTRY serviceTable[] =

{

{ SERVICE_NAME,

(LPSERVICE_MAIN_FUNCTION) ServiceMain},

{ NULL, NULL }

};

BOOL success;

// Register with the SCM

success =

StartServiceCtrlDispatcher(serviceTable);

if (!success)

ErrorHandler("In StartServiceCtrlDispatcher",

GetLastError());

}

Код, реализующий установку сервиса.

//***************************************************************

// From the book "Win32 System Services: The Heart of Windows NT"

// by Marshall Brain

// Published by Prentice Hall

//

// This code installs a service.

//***************************************************************

// install.cpp

#include <windows.h>

#include <iostream.h>

void ErrorHandler(char *s, DWORD err)

{

cout << s << endl;

cout << "Error number: " << err << endl;

ExitProcess(err);

}

void main(int argc, char *argv[])

{

SC_HANDLE newService, scm;

if (argc != 4)

{

cout << "Usage:\n";

cout << "install service_name \

service_label executable\n";

cout << "service_name is the \

name used internally by the SCM\n";

cout << "service_label is the \

name that appears in the Services applet\n";

cout << "(for multiple \

words, put them in double quotes)\n";

cout << "executable is the \

full path to the EXE\n\n";

return;

}

// open a connection to the SCM

scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);

if (!scm) ErrorHandler("In OpenScManager",

GetLastError());

// Install the new service

newService = CreateService(

scm, argv[1], // eg "beep_srv"

argv[2], // eg "Beep Service"

SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,

SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,

argv[3], // eg "c:\winnt\xxx.exe"

0, 0, 0, 0, 0);

if (!newService) ErrorHandler("In CreateService",

GetLastError());

else cout << "Service installed\n";

// clean up

CloseServiceHandle(newService);

CloseServiceHandle(scm);

}

Удаление сервиса.

//***************************************************************

// From the book "Win32 System Services: The Heart of Windows NT"

// by Marshall Brain

// Published by Prentice Hall

//

// This code removes a service from the Services applet in the

// Control Panel.

//***************************************************************

// remove.cpp

#include <windows.h>

#include <iostream.h>

void ErrorHandler(char *s, DWORD err)

{

cout << s << endl;

cout << "Error number: " << err << endl;

ExitProcess(err);

}

void main(int argc, char *argv[])

{

SC_HANDLE service, scm;

BOOL success;

SERVICE_STATUS status;

if (argc != 2)

{

cout << "Usage:\n remove service_name\n";

return;

}

// Open a connection to the SCM

scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);

if (!scm) ErrorHandler("In OpenScManager",

GetLastError());

// Get the service's handle

service = OpenService(scm, argv[1],

SERVICE_ALL_ACCESS | DELETE);

if (!service) ErrorHandler("In OpenService",

GetLastError());

// Stop the service if necessary

success = QueryServiceStatus(service, &status);

if (!success) ErrorHandler("In QueryServiceStatus",

GetLastError());

if (status.dwCurrentState != SERVICE_STOPPED)

{

cout << "Stopping service...\n";

success = ControlService(service,

SERVICE_CONTROL_STOP, &status);

if (!success) ErrorHandler("In ControlService",

GetLastError());

}

// Remove the service

success = DeleteService(service);

if (success) cout << "Service removed\n";

else ErrorHandler("In DeleteService",

GetLastError());

// Clean up

CloseServiceHandle(service);

CloseServiceHandle(scm);

}