![]() (C) Зайцев Олег 1998-2001 |
Программирование на Delphi
|
| Система | Реестр | Графика | Сети | Мультимедиа | WEB | Разработка_компонент | Железо | Прочее |
Управление приложением через TelnetЭта статья содержит подробное описание принципов построения Telnet - сервера, который может применяться для удаленного управления и администрирования программы. Внимание !! Сайт переехал - он теперь расположен по адресу http://z-oleg.com/delphi, размещенные там материалы переработаны и дополнены. На z-ol.chat.ru остается копия, однако обновляться она больше не будет |
|
|
|
(C) Зайцев Олег, 2001
Итак, начнем с главного - почему для удаленного администрирования своей программы следует использовать именно Telnet ? Ответ на этот вопрос достаточно прост:
Рассмотрим немного теории. Утилиту Telnet легче всего запустить через Start->Run
(Пуск -> Выполнить). После запуска необходимо произвести соединение с удаленным
хостом, для чего выполняется используется меню "Connect->Remote System". При этом
выводится меню соединения, в котором необходимо указать три параметра: хост,
порт и тип терминала. В качестве хоста указывается имя удаленного компьютера
(или его IP адрес), порт можно задать двумя путями - выбором/вводом символического
имени (например, telnet), или вводом номера порта. Мы будем пользоваться вторым
путем, т.е. будем использовать нестандартные номера портов. Тип терминала
оставим vt100.
Утилита Telnet поддерживает параметры командой строки:
telnet [remote_host] [port]где
telnet zaitsevov или telnet zaitsevov 5000Протокол Telnet очень прост - сначала устанавливается TCP/IP соединение с удаленной машиной. Затем, когда пользователь вводит символ, происходит его передача удаленному хосту. Для простоты будем называть его сервером.
Итак, приступим к разработке приложения. Создадим пустой проект и поместим на форму компонент ServerSocket1 типа TServerSocket. Зададим ему порт, например 5000. Напоминаю, что:
begin
try
ServerSocket1.Active := true;
except
ShowMessage('Ошибки при активации ServerSocket');
end;
end;
Далее необходимо научиться определять моменты соединения и отключения клиента.
Для этого следует создать обработчики OnClientConnect и OnClientDisconnect.
Сразу отмечу, что при подключении клиента обычно принято выдывать ему заголовок,
ообщающий о том, что он соединился с программой *** версии NN. С учетом этого
обработчик OnClientConnect будет иметь вид:
procedure TMain.ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Socket.SendText('Connected. Программа Telnet1 Example на проводе.'+#$0D+#$0A);
Socket.SendText('Enter password : ');
Connected := false;
Memo1.Lines.Add('Произошло соединение с пользователем');
end;
При этом я хочу подчеркнуть особенность - нормально поддерживается одно
соединение, для нескольких необходимы некоторые усложнения и мых их пока опустим.
// Поддержка связи по TCP/IP для удаленного конфигурирования - действия при отключении
procedure TMain.ServerSocket1ClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Connected := false;
Memo1.Lines.Add('Соединение разорвано');
end;
Итак, теперь настало время для самого интересного - написания обработчика OnClientRead.
Этот обработчик вызывается всякий раз, когда от клиента приходят данные. Т.е. в
свете приведенных выше теоретических замечаний это будет происходить при вводе каждого
отдельного символа. Задачи обработчика:
// Поддержка связи по TCP/IP для удаленного конфигурирования - действия при получении данных procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); var s, st : string; begin s := Socket.ReceiveText; // Это код перевода строки ? Если да, то выполняем команду и передаем ее ответ клиенту if ord(s[1]) = $0D then begin st := ExecuteCMD(TelnetS); if st <> '' then st := #$0D + #$0A + st; st := st + #$0D + #$0A + '>'; TelnetSendText(Socket, st); TelnetS := ''; exit; end; // Это код клавиши BackSpace. Если да, то передадим его клиенту // и удалим последний символ из буфера if ord(s[1]) = $08 then begin Delete(TelnetS, length(TelnetS), 1); TelnetSendText(Socket, s); exit; end; // Добавим очередной символ к буферу TelnetS := TelnetS + s; // Передадим его клиенту для организации эха if connected then TelnetSendText(Socket, s); end;Как легко заметить, приведенный выше код реализует эхо, обрабатывает BackSpace и дожидается ввода команды, считая код $OD (Enter) признаком завершения ввода команды. При обнаружении этого кода вызывается функция пользователя ExecuteCMD, которая должна разобрать и проанализировать команду, выполнить ее и вернуть (при необходомости) ответ пользователю. Эта же функция занимается проверкой вводимого пользователем пароля. Так ка передача ответа/эха имеет некоторые особенности, например, необходимость удвоения символа с кодом FF и подавления передачи для реализации невидимого ввода, имеет смысл выполнить ее в виде отдельной функции:
// Передача ответа/эха клиенту function TForm1.TelnetSendText(Socket: TCustomWinSocket; AText: string): boolean; var i : integer; St : string; begin Result := false; if Not(connected) then exit; St := ''; for i := 1 to length(AText) do if AText[i] <> #$FF then st := st + AText[i] else st := st + #$FF + #$FF; Socket.SendText(st); end;В моем примере функция ExecuteCMD имеет вид:
// Интерретатор команд
function TForm1.ExecuteCMD(ACmd: string): string;
var
UCmd, Params : string;
begin
Result := '';
Memo1.Lines.Add('Выполняется : '+ACmd);
if Not(connected) then begin
if UpperCase(ACmd) = '123' then begin
Connected := true;
Result := 'Пользователь идентифицирован !';
end;
exit;
end;
// Выделение команды
UCmd := ACmd;
Params := '';
if pos(' ', UCmd) > 0 then begin
Params := Copy(UCmd, pos(' ', UCmd)+1, Length(UCmd));
UCmd := Copy(UCmd, 1, pos(' ', UCmd)-1);
end;
UCmd := Trim(UpperCase(UCMD));
Memo1.Lines.Add('Выделена команда : '+UCmd);
// ? или HLP или HELP - вывод справки
if (UCmd = '?') or (UCmd = 'HLP') or (UCmd = 'HELP') then begin
Result :=
'Краткая справка по командам Telnet интерфейса'+CRLF+
' ?, HLP, HELP - вызов справки'+CRLF+
' EXIT - завершение работы по Telnen интерфейсу'+CRLF+
' HALT - немедленный останов программы'+CRLF+
' VER - версия программы'+CRLF+
' MESS <собщение> - вывод сообщения для пользователя'+CRLF+
' INP <собщение> - вывод сообщения для пользователя и возврат его ответа';
exit;
end;
if (UCmd = 'EXIT') then begin
ServerSocket1.Socket.Connections[0].Close;
exit;
end;
if (UCmd = 'VER') then begin
Result := 'Версия 1.00 от 27.01.2001 (C) Зайцев Олег';
exit;
end;
if (UCmd = 'HALT') then begin
halt;
end;
if (UCmd = 'MESS') then begin
ShowMessage(Params);
exit;
end;
if (UCmd = 'INP') then begin
Result := InputBox(Params,'Введите ответ','');
exit;
end;
Result := 'Неизвестная команда '+ACmd;
end;
Реальная система команд естественно определяется разработчиком, но рекомендуется
предусмотреть следующие команды:
procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin // Обработка события "разрыв соединения" if ErrorCode = 10054 then begin Socket.Close; ErrorCode := 0; end; end;И в завершении хочется сказать, что подобная система внедрена в несколько моих программ, испрользуемых в ОАО Смоленскэнерго и отлично себя зарекомендовала, т.к. предприятие большое и возможность удаленной настройки/управления в ряде случаев освобождает разработчика от ненужной беготни. Исходные тексты примера, обсуждаемого в данной статье, можно скачать - telnet1.zip (4 кб).
Использование данных материалов и их размещение на других сайтах возможно только с согласия автора.
Я советую посетить и другие сайты, посвященные программированию. Это легко сделать по кольцу:
Algorithm project
: Кольцо сайтов, посвященных программированию (подробнее о проекте WebRing...) [ Предыдущие 5 сайтов | Предыдуший | Следующий | Следующие 5 сайтов | Выбрать сайт случайным образом | Список всех сайтов ]