Быстродействие. Все равно быстрее будет работать чем на сях и delphi (не говоря уже о тормознутом визуал бейсике. Такой язык надо в школе изучать - это надо ж, грузить dll-и и запускать с них функции); Размеры. 11 kb "всунуть" в что-то все таки легче чем, например, 100; Если мы пишем на асме, то имеем дело с ошибками своими и мелкософта, а не с глюками программера из фирмы Borland. Взять хотя-бы "RadioGroup" в Delphi; В ранних версиях "DTr", периодически возникала ошибка 10060 асинхронной работы. Только на некоторых компьютерах, но все-таки. Написание сервера на сях не помогло. После выхода версии на асме и сооружения в клиенте процесса обработки очереди приходящих сообщений, у меня еще такой ошибки не возникало; В своих продуктах фирмы по производству программного обеспечения, любят при ошибке вызывать исключительную ситуацию, на что виндоуз реагирует показом "красивого" окошка с сообщением об ошибке. Некоторые такие реакции не подавляются try-except. Для сервера это, мягко сказать, нежелательно. В асме все проще; Для борьбы с буржуйскими программами с помощью WINdasm, SoftIce и т.п., ассемблер надо знать. А для того чтобы его знать, на нем иногда надо писать. Завести 2-ой комп и бегать, смотреть на одном окно SoftIce, а на другом доки - это сильно круто.
Теперь продолжим разговор о нашем клиенте. Как я уже говорил, процесс обработки очереди при разсоединении и выходе из проги надо прерывать. Для этого модифицируем нашу процедуру "ClientSocket1Disconnect", вставив в нее вот такой код:
if not RecvThread.Terminated then begin while not RecvThread.Terminated do begin try RecvThread.Terminate; except end; sleep(100); Application.ProcessMessages; end; end; LstRbeg:=nil; LstRend:=nil;
Также нас интересует событие, происходящее перед выходом из клиента. Выделим нашу форму ("Form1"), перейдем в "Object Inspector" на закладку "Events" и 2 раза "click"-нем по "onClose". Перейдем в раздел кода и запишем:
// Выход из проги procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caNone; if constat then Form1.Button1Click(Sender); Application.ProcessMessages; Application.Terminate; Halt(1); end;
Теперь поговорим о визуализации и удобстве работы с клиентом. Кнопки, которые отвечают за посылание серверу команд, надо как-то выделить. Для этого воспользуемся объектом "ImageList" с закладки "Win32" ("ImageList1"). Помещаем его на форму и с помощью правой кнопки мыши добавляем в него изображения для кнопок. Теперь нужно выделить "ToolBar1" и в его свойстве "Images", из всплывающего списка, поставить "ImageList1". После этого перейдем на "ToolButton1", в свойстве "ImageIndex", выберем нужный рисунок. Для отображения "всплывающей" подсказки в свойстве "ShowHint" поставим "true", а в свойстве "Hint", напишем "Кнопка № 1". Очень информативно.
Сканер для сервера.
Допустим, мы знаем, что серверная часть запущена у человека (это звучит гордо), пользующегося услуами провайдера "Slow". Мы также знаем пространство адресов этого провайдера. Но мы не знаем, какой адрес даст провайдер этому челу.
Или у нас есть локалка. Почему-то иногда прога путает адреса на плюс-минус два. Почему - до сих пор не знаю (данная проблема была замечена не мной), но если кто знает, то пусть отпишет в "Отзывы".
Или мы изменили порт, а какой забыли.
Для всего этого нам нужен сканер по адресам и портам. То, о чем пойдет речь далее, можно использовать не только для нашего сервера. Итак, в Delphi, в проекте нашего сервера, выбираем в верхнем меню "File"-->"New Form". Пусть это будет "Form2". В свойстве "Caption" пишем "Сканер". Размещаем на форме компоненты:
Edit1 С какого порта начинать сканирование; Edit2 По какой порт; Edit3 Первые 3 цифры адреса в виде "xxx.xxx.xxx.xxx" (без точки в конце); Edit4, Edit5 Диапазон последней цифры адреса; Edit6 Время ожидания соединения (в секундах); Button1 Начать/прекратить сканирование; Memo1 Отчет сканирования; ProgressBar1, ProgressBar2 (Win32) Для визуализации процесса перебора по адресам и портам соответственно; ClientSocket1 И так понятно.
Теперь на "Form1" лепим кнопку, обзываем ее "Scaner" и нажимаем на ней два раза. В разделе кода пишем :
В раздел "uses" добавляем "Unit2". Переходим на "Form2". Два раза нажимаем на "Button1", на события "onConnect" и "onError" в "ClientSocket1" и на "onClose" в "Form2". Вот текст модуля "Unit2.pas":
//Close Scaner procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin // если запущен, то прерываем процесс if Rez11 then begin Action := caNone; Form2.Button1Click(Sender); end; end;
// Включить/отключить сканер procedure TForm2.Button1Click(Sender: TObject); var I, J, K: Integer; DopStr: string; begin if Rez11 then begin // прервать сканирование if Application.MessageBox('Прервать сканирование?', 'Сканер', mb_YesNo + mb_IconQuestion) = idYes then begin Rez11 := false; Bool := false; end; end else begin // запуск сканера if StrToInt(Form2.Edit2.Text) < StrToInt(Form2.Edit1.Text) then begin Application.MessageBox('Неверно указан диапазон для портов','Сканер', mb_Ok + mb_IconStop); exit; end;
if StrToInt(Form2.Edit5.Text) < StrToInt(Form2.Edit4.Text) then begin Application.MessageBox('Неверно указан диапазон IP-адресов','Сканер', mb_Ok + mb_IconStop); exit; end;
// цикл по адресам while I <= StrToInt(Form2.Edit2.Text) do begin J := StrToInt(Form2.Edit4.Text); // цикл по портам while J <= StrToInt(Form2.Edit5.Text) do begin Application.ProcessMessages; if not Rez11 then break; Form2.ClientSocket1.Active := false; Form2.ClientSocket1.Port := I; Form2.ClientSocket1.Address := trim(DopStr) + '.' + trim(IntToStr(J));
try // попытка соедениться Form2.ClientSocket1.Active := true; Application.ProcessMessages;
// время ожидания Bool := true; K := round(StrToInt(Form2.Edit6.Text) * 1000 / 50); while Bool do begin Sleep(50); Application.ProcessMessages; dec(K); if K=0 then begin
Form2.ProgressBar1.Position := Form2.ProgressBar1.Position + 1; end; Form2.ProgressBar2.Position := Form2.ProgressBar1.Position + 1; Form2.ProgressBar1.Position := Form2.ProgressBar1.Position + 1; except Application.MessageBox('Ошибка выполнения операции', 'Сканер', MB_Ok + mb_IconStop); end; Form2.Button1.Caption := 'Сканер'; Form2.ProgressBar1.Position := 0; Form2.ProgressBar2.Position := 0; Form2.Caption := 'Сканер по адресам и портам'; if Rez11 then begin Application.MessageBox('Процедура сканирования по адресам и портам закончена.', 'Сканер', mb_Ok + mb_IconAsterisk); Form2.Memo1.Lines.Add('-----------------'+#13+#10+'========== ВСЕ АДРЕСА И ПОРТЫ ОТСКАНИРОВАНЫ'+#13+#10+#13+#10); Rez11 := false; end else Form2.Memo1.Lines.Add('------------'+#13+#10+'============== ПРЕРВАНО НА порт- '+IntToStr(I)+', адрес-'+trim(DopStr)+'.'+IntToStr(J-1)+#13+#10+#13+#10); except Application.MessageBox('Ошибка инициализации процесса.','Сканер',mb_Ok+mb_IconStop); end; Form2.Caption:='Сканер по адресам и портам'; end; end;
// Есть ответ сервера procedure TForm2.ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket); begin // если соеденились вывести сообщение Form2.Memo1.Lines.Add('***' + #13 + #10 + 'Порт: ' + IntToStr(Form2.ClientSocket1.Port) + ' ' + 'Адрес: ' + Form2.ClientSocket1.Address + ' - ЕСТЬ ОТВЕТ' + #13 + #10); Application.ProcessMessages; // прервать время ожидания try Form2.ClientSocket1.Active := false; except end; Bool := false; end;
// Ошибка при соединении procedure TForm2.ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin // прервать время ожидания если ошибка ErrorCode := 0; Bool := false; end;
end.
Теперь проверим. Запускаем сервер и клиент. Жмем кнопку "Сканер". В "Edit1" пишем "10001", в "Edit2" - "10001", в "Edit3" - "127.0.0", в "Edit4" - "1", в "Edit5" - "254", в "Edit6" - "1". Все значения без кавычек. Жмем нашу кнопку начала сканирования. Все, проверка закончена.
P.S. Статья и программа предоставлена в целях обучения и вся ответственность за использование ложится на твои хилые плечи.