|
Управление программой через Telnet интерфейс
|
|
Главная страница \ WEB программирование \ Управление программой через Telnet интерфейс |
Общие вопросы |
Рассмотрим немного теории. Утилиту Telnet легче всего запустить через Start->Run
(Пуск -> Выполнить). После запуска необходимо произвести соединение с удаленным
хостом, для чего выполняется используется меню "Connect->Remote System". При этом
выводится меню соединения, в котором необходимо указать три параметра: хост,
порт и тип терминала. В качестве хоста указывается имя удаленного компьютера
(или его IP адрес), порт можно задать двумя путями - выбором/вводом символического
имени (например, telnet), или вводом номера порта. Мы будем пользоваться вторым
путем, т.е. будем использовать нестандартные номера портов. Тип терминала
оставим vt100.
Утилита Telnet поддерживает параметры командой строки:
telnet [remote_host] [port]где
telnet zaitsevov или telnet zaitsevov 5000Протокол Telnet очень прост - сначала устанавливается TCP/IP соединение с удаленной машиной. Затем, когда пользователь вводит символ, происходит его передача удаленному хосту. Для простоты будем называть его сервером.
Пример Telnet интерфейса |
Итак, приступим к разработке приложения. Создадим пустой проект и поместим на форму компонент 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;И в завершении хочется сказать, что подобная система внедрена в несколько моих программ, испрользуемых в ОАО Смоленскэнерго и отлично себя зарекомендовала, т.к. предприятие большое и возможность удаленной настройки/управления в ряде случаев освобождает разработчика от ненужной беготни. Исходные тексты примера, обсуждаемого в данной статье, можно скачать - examples\telnet1.zip (4 кб).