Главная > Программирование, системное > Delphi: Контроль соединений c Internet.

Delphi: Контроль соединений c Internet.

Причиной побудившей меня написать эту статью была нестабильность работы соединения с Интернетом, которое довольно часто разрывалось. А стандартная функция ОС Windows ХР не справлялась с возложенной на нее обязанностью по восстановлению разорванного соединения. Плюс к этому, не так уж удобно «листать» системный журнал в поиске причины разрыва, или времени разрыва, или других каких логов.(Стоит только вспомнить эти ужасные времена, когда у «стрима» было аж по 4 обрыва. Сколько нерв попортили они людям) … прошу прощения, отвлекся ;) Приступим к решению этого вопроса. В качестве язык программирования будем использовать Delphi 7 версии.

Первое что нам надо рассмотреть, это средства для работы с системой установки удаленного доступа (RAS). Данное средство представляет собой набор API-функций, которые хранятся в системной библиотеке rasapi.dll, используемой ОС.

Это достаточно мощная библиотека, и для решения нашей проблемы нам понадобятся не все функции. Поэтому мы рассмотрим только необходимые нам. Перечислю их:

  • RasEnumConnections – функция для проверки наличия установленных соединений;
  • RasEnumEntries – функция получения списка соединений(зарегистрированных) в системе;
  • RasDial – функция установки соединения.

Что ж, теперь необходимо сформулировать тех. задание решения поставленной задачи. И так:

  1. Необходимо определить/получить список соединений (зарегистрированных в системе)
  2. Реализовать процедуру дозвона
  3. Реализовать обработчик-таймер, который будет проверять, через определенный интервал времени, статус установленного соединения
  4. Написать процедуру ведения лога – пользовательский журнал.

Кроме того, чтобы программы была более гибкой, то мы реализуем дружественный интерфейс, в котором позволим пользователю выбирать из списка соединений (которых может быть более одного ;) ). Поэтому начнем мы с последнего.

Для этого запустим Delphi, создадим новый проект и сохраним его под названием, к примеру ControlConn. Скинем на форму:

  • 2 ListBox’a;
  • 3 кнопки;
  • 1 поле ввода для указания интервала проверки статуса соединения.

Теперь проведем некоторое приготовление. Один ListBox назовем ActivCon – в него будут занесены сведения о существующих соединениях, а второй назовем ControlCon – в нем будут находится соединения которые необходимо контролировать. Одна из кнопок будет отвечать за запуск процесса контроля – и будет иметь не двусмысленное имя start, оставшиеся 2-е кнопки необходимы для манипулирования содержимым ListBox’ов, добавить в список контролируемых соединений и удалить из списка. Поле ввода назовем interval.

Создадим для формы событие OnActivate, на вкладке Event в инспекторе объектов. В нем мы опишем код, который будет получать список соединений. Кстати, замечу, для тех кто только начинает и тех кто этого не знал, но программирует уже давно, что хорошим стилем программирования является описание всех своих функций и процедур, используемых в проекте, в отдельном модуле. В данном случае у нас нет необходимости выносить код в отдельную функцию. Но стоит иметь в виду, что в этом есть два основных плюса:

  • сам проект не перегружен большим количеством программного кода,
  • в случае необходимости редактирования некоторых функция проекта, гораздо удобнее загрузив в редактор отдельный модуль.

При этом не возникнет ни каких проблем с согласованием с промежуточным кодом в самом проекте.

А теперь код:

procedure TForm1.FormActivate(Sender: TObject);
var
bSize : integer; //размер массива, который будут помещаться сведения об «удаленных соединениях»
cCount : integer; //количество удаленных соединений
cList : array[1..MaxEntries] of TRasEntryName; //массив, в который будут помещены сведения об удаленных соединениях
// где MaxEntries – константа, представляющая максимально возможное количество соединений, объявлена в модуле RasUnit.pas
x,rslt : integer;
begin
cList[1].dwSize:=SizeOf(TRasEntryName);
bSize:=SizeOf(TRasEntryName)*MaxEntries;
//получаем список соединений
rslt:=RasEnumEntries(nil, nil, @cList[1], bSize, cCount);
//если функция выполнилась успешно и есть соединения
if (rslt=0) and (cCount>0) then
begin
// то заполняем список соединений ActivCon
for x:=1 to cCount do
ActivCon.Add(cList[x].szEntryName);
end;
end;

Теперь при запуске программы у нас в списке ActivCon будут перечисленный все зарегистрированные в системе ras-соединения. Далее нужно добавить возможность манипулирования списком соединений. Следующий код для кнопки «Добавить»:


procedure TForm1.Button1Click(Sender: TObject);
var
i,j:integer;
begin
if ActivCon.SelCount=0 then
exit;
for j:=0 to ActivCon.SelCount-1 do
if search(ActivCon.Items.Strings[ActivCon.ItemIndex], ControlCon) = -1 then
begin
ControlCon.Items.Add(ActivCon.Items.Strings[ActivCon.ItemIndex]);
ActivCon.Items.Delete(ActivCon.ItemIndex);
end
else
begin
Application.MessageBox('Такое соединение в списке уже есть!','Внимание');
end;
end;

Для кнопки «Удалить»:

procedure TForm1.Button2Click(Sender: TObject);
var
i,j:integer;
begin
if ControlCon.SelCount=0 then
exit;
for j:=0 to ControlCon.SelCount-1 do
if search(ControlCon.Items.Strings[ControlCon.ItemIndex], ActiveCon) = -1 then
begin
ActiveCon.Items.Add(ControlCon.Items.Strings[ControlCon.ItemIndex]);
ControlCon.Items.Delete(ControlCon.ItemIndex);
end
else
begin
Application.MessageBox('Такое соединение в списке уже есть!','Внимание');
end;
end;

В обоих листингах используется функция search, которой в качестве параметра передается строка, наличие которой необходимо проверить в списке соединений:


function search(str:string; lsb:TListBox):integer;
var
i,j:integer;
begin
for i:=0 to lsb.Count-1 do
begin
j:=i;
if lsb.Items[i] = str then
begin
search:=j;
exit;
end;
end;
search:=-1;
end;

Да, кстати, самое главное то мы забыли. Нам еще нужен таймер, добавим его. Кроме того нам необходимо объявить глобальные переменные и структуру состояния соединения, объявим их:

type
TMyDialParam = Record
AMsg:Integer;
AState:TRasConnState;
AError:Integer;
End;

var
MyDialParam:TMyDialParam;
hRas: ThRASConn;

Кроме глобальных переменных, нам нужно определить CallBack-процедуру, которая не обходима для вызова функции RasDial().


procedure RasCallback(msg: Integer; state: TRasConnState; error: Integer); stdcall;
begin
MyDialParam.AMsg:=msg;
MyDialParam.AState:=state;
MyDialParam.AError:=error;
{
Здесь можно описать обработчик полученных сообщений о статусе установки соединения.
}
end;

Теперь создадим для Таймера событие onTimer и напишем в нем следующий код:


procedure TForm1. Timer1Timer(Sender: TObject);
var
rcArray: array[1..100]of TRASCONN;
rStatus: TRASCONNSTATUS;
dwRet: integer;
rcsize: integer;
dw2: integer;
i,j,tr:integer;
s:string;

begin
tr:=0;
//отключаем таймер
Timer1.Enabled:=false;
if Edit1.Text = ‘’ then // Если интервал не указан
Timer1.Interval:=5000; // то устанавливаем значение по умолчанию 5 секунд
else
Timer1.Interval:=StrToint(Edit1.Text)*1000;
//производим подготовку для получения списка работающих соединений(уже установленных)
rcsize:= sizeof(rcArray);
ZeroMemory(@rcArray, rcsize);
rcArray[1].dwSize := sizeof(TRASCONN);
dwRet := RasEnumConnections(@rcArray, rcsize, dw2);
ZeroMemory(@rStatus, sizeof(TRASCONNSTATUS));
rStatus.dwSize := sizeof(TRASCONNSTATUS);
// если количество работающих соединений равно 0, тогда
if dw2 = 0 then //запускаем их поочередно
begin
if ControlCon.Count <> 0 then // проверяем не пуст ли список контролируемых соединений
for i:=1 to ControlCon.Count-1 do
begin
MemError.Lines.Add(TimeToStr(Time)+' соединение не установлено '+ ControlCon.items[i]+'. Запускаю.'); //заносим в журнал сообщение о разрыве
//и вызываем функцию установки соединения
ConnectEntry(ControlCon.Items[i],i);
end;
end
else
begin
//если же было определено что соединение хоть одно но запущено
if ControlCon.Count <> 0 then // то мы опять же проверяем список
begin
for i:=0 to ControlCon.Count-1 do // после чего пробегаем по всему списку и сверяемся с полученным списком рабочих соединений, если в полученном списке какие то соединения отсутствуют, то мы их немедленно устанавливаем
begin
for j:=1 to dw2 do
begin
if ControlCon.Items[i] = rcArray[j].szEntryName then
begin
tr:=1;
MemError.Lines.Add(TimeToStr(Time)+' соединение не установлено '+ ControlCon.items[i]+'. Запускаю.'); //заносим в журнал сообщение о разрыве
end;
end;
if tr = 1 then
// устанавливаем соединение
ConnectEntry(ControlCon.Items[i],i);
end;
end;
end;
//запускаем таймер
Timer1.Enabled:=true;
end;

Теперь опишем процедуру установки соединения:

procedure ConnectEntry(nameEntry: string);
var
fl : LongBool;
r : Integer;
DialParams : TRasDialParams;
hR: ThRASConn;
begin
//забиваем DialParams нулями
FillChar(DialParams, SizeOf(TRasDialParams), 0);
//Инициализируем переменную DialParams, заполнив поля dwSize и szEntryName
with DialParams do
begin
dwSize:=Sizeof(TRasDialParams);
StrPCopy(szEntryName, nameEntry);
end;
//Теперь вызовим функцию для заполнения оставшихся полей структуры DialParams
r:=RasGetEntryDialParams(nil, DialParams, fl);
//если все прошло успешно
if r=0 then
begin
//вызываем функцию «дозвона»
r:=RasDial(nil, nil, DialParams, 0, @RasCallback, hRas);
end;
end;

На этом все. Статью заканчивал в спешке, поэтому в случае обнаружения каких либо не точностей, не поняток или ошибок просьба сильно не ругаться, а отписываться по всем замечания и не только ;). Всем спасибо!

Необходимые модули:
rasunit (тут была ссылка на скачивание, но она стала битой)

Пожалуйста, оцените полезность и качество данной статьи. Одна звезда - плохо, 5 - хорошо.
1/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.2/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.3/5. Мы будем признательны, если вы напишете комментарий с причиной низкой оценки.4/5.5/5. (1 голосов, средний: 5,00 из 5)
Загрузка...
  1. pomashok
    24 января 2009 в 23:13 | #1

    Здорово! Интересно! Меня эта проблема тоже мучает давно (нестабильность работы соединения с интернетом).
    Было бы неплохо добавить информацию по расшариванию подключения (для случая когда машинка является "сервером") и ссылку на исходник (в котором будет уже RasUnit.pas) :)

  2. C0ffe1n
    25 января 2009 в 10:14 | #2

    Отличное предложение! ;) Сегодня же займусь этим вопросом...

  3. pomashok
    26 января 2009 в 00:12 | #3

    а исходник проги есть? Что-то не получается повторить код (ругается) :(

    • C0ffe1n
      26 января 2009 в 01:16 | #4

      Да, у нас есть наша реализация, ее можно скачать от сюда. Версия вполне рабочая, но требующая тестирования. Вот и протестируй ;) Кстати, советую тебе зарегаться на нашем форуме и в случае возникновения вопросов отписываться там;)

  4. pomashok
    10 февраля 2009 в 22:29 | #5

    Обязательно зарегистрируюсь! Сейчас катастрафически не хватает времени.
    Исходники и статья совершенно разные!

  5. C0ffe1n
    10 февраля 2009 в 22:44 | #6

    @pomashok
    Да, есть такое дело. В статье просто используется старая версия, но замечу рабочая. А то что выложено в "Наши проекты", на то что дал тебе ссылку уже более отточены, но увы требуют доработки и расширения. Поэтому и отличаются. Не вижу смысла редактировать статью ибо в ней хоть и старая версия, но смысл тот же. В целом то ничего не изменилось ;)

  6. pomashok
    14 февраля 2009 в 09:43 | #7

    Я к тому, что статья и ссылка на исходники должны быть идентичны.
    Чтоб кто прочитал статью мог воспользоваться исходником. Оттачивать проги будут своих приложений... :)

  7. samsim
    19 июня 2014 в 13:20 | #8

    Контроль не так часто нужен, как безопасность самого соединения. Контролировать можно только то, что требует такого контроля. Статья хорошая, иногда может понадобится.

  1. Пока что нет уведомлений.