Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2011.01.16;
Скачать: [xml.tar.bz2];

Вниз

Утечка памяти при мультипоточном сканировании сети NetShareEnum   Найти похожие ветки 

 
Josh ©   (2009-01-30 00:13) [0]

Есть приложение, которое выполняет функции сканера общих ресурсов в
локальной сети (наподобие LanScope). Задается диапазон IP-адресов, который
нужно сканировать. Для сканирования каждого компьютера в этом диапазоне
создается отдельный поток, который пингует компьютер и в случае, если ответ
пришел, с помощью функции NetShareEnum пытается узнать доступные ресурсы.

Так вот, после каждого сканирования диапазона адресов занимаемая память
приложения (см. в диспетчере задач) увеличивается на 200-300 КБ по
необъяснимым причинам. Для отслеживания утечки памяти использовал FastMM4
(Fast Memory Manager), но он молчит. Возможно, я его неправильно настроил
для работы с мультипоточным приложением.

Помогите, пожалуйста, определить и устранить причину таинственной "утечки"
памяти.

Ниже привожу упрощенный рабочий, но проблемный код. Для запуска на форме
должна находится кнопка Button1 - для начала сканирования диапазона и поле
Memo1 - для вывода списка найденных общих ресурсов (желательно в Memo1
вставить вертикальный скроллинг). Диапазон адресов изменяется в тексте
программы, по умолчанию стоит от 192.168.1.1  до192.168.1.254.


 
Josh ©   (2009-01-30 00:13) [1]

----------------------------------- Листинг (часть 1) ----------------------

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, WinSock, StdCtrls;

type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

const
 WSA_TYPE   = $101;
 IP_SUCCESS = 0;
 SHARETYPE_DISKTREE = 0;

type
 TIPAddress = array [1 .. 4] of Byte;
 TIPAddressDynArray = array of TIPAddress;

 TShareInfo = record
   Name : String;
   Path : String;
 end;
 TShareInfoDynArray = packed array of TShareInfo;

 TSharedComputer = record
   IPAddress  : TIPAddress;
   SharesList : TShareInfoDynArray;
 end;
 TSharedComputerDynArray = packed array of TSharedComputer;

 TThreadScanResources = class(TThread)
 private
   Started          : Boolean;
   IPAddress        : TIPAddress;
   ShowHiddenShares : Boolean;
   SharesList       : TShareInfoDynArray;
 protected
   procedure Execute; override;
 end;

 TShareInfo1 = packed record
   SHI1_NetName : PWChar;
   SHI1_Type   : DWORD;
   SHI1_Remark  : PWChar;
 end;
 TShareInfo1Array = packed array [1 .. 512] of TShareInfo1;
 PShareInfo1Array = ^TShareInfo1Array;

 IPOptionInformation = packed record
   TTL         : Byte;
   TOS         : Byte;
   Flags       : Byte;
   OptionsSize : Byte;
   OptionsData : Pointer;
 end;

 ICMPEchoReply = packed record
   Address  : Integer;
   Status   : Integer;
   RTTime   : Integer;
   DataSize : Word;
   Reserved : Word;
   Data     : Pointer;
   Options  : IPOptionInformation;
 end;

 PIPINFO = ^IPOptionInformation;
 PVOID   = Pointer;

function NetShareEnum(ServerName: PWideChar; Level: DWORD; BufPtr: Pointer;
                     PrefMaxLen: DWORD; EntriesRead, TotalEntries,
                     Resume_Handle: LPDWORD): DWORD;
                     stdcall; external "NETAPI32.DLL";

function ICMPCreateFile: THandle; stdcall; external "ICMP.DLL" Name
"IcmpCreateFile";

function ICMPCloseHandle(IcmpHandle: THandle): Bool; stdcall; external
"ICMP.DLL" Name "IcmpCloseHandle";

function ICMPSendEcho(ICMPHandle: THandle; DestAddress: u_long; RequestData:
PVOID;
                     RequestSize: Word; RequestOptns: PIPINFO; ReplyBuffer:
PVOID;
                     ReplySize: DWORD; Timeout: DWORD): DWORD; stdcall;
external "ICMP.DLL" Name "IcmpSendEcho";


 
Josh ©   (2009-01-30 00:14) [2]

----------------------------------- Листинг (часть 2) ----------------------

procedure Delay(ms: Longint);
{$IFNDEF WIN32}
var
 TheTime: Longint;
 {$ENDIF}
begin
 {$IFDEF WIN32}
 Sleep(ms);
 {$ELSE}
 TheTime:= GetTickCount + ms;
 while GetTickCount < TheTime do Application.ProcessMessages;
 {$ENDIF}
end;

procedure StrToPWChar(S: String; var Buffer: PWChar; BufferSize: Byte =
255);
var
 L: Integer;
begin
 L:= SizeOf(WideChar) * BufferSize;
 GetMem(Buffer, L);
 StringToWideChar(S, Buffer, L);
end;

function FullIP(IPSegment1, IPSegment2, IPSegment3, IPSegment4: Byte):
TIPAddress;
begin
 Result[1]:= IPSegment1;
 Result[2]:= IPSegment2;
 Result[3]:= IPSegment3;
 Result[4]:= IPSegment4;
end;

function IPToStr(IPAddress: TIPAddress): String;
begin
 Result:= IntToStr(IPAddress[1]) + "." + IntToStr(IPAddress[2]) + "." +
          IntToStr(IPAddress[3]) + "." + IntToStr(IPAddress[4]);
end;

function FullNetSharePath(RemoteHost, ShareName: String): String;
begin
 Result:= "\\" + RemoteHost + "\" + ShareName + "\";
end;

function PingComputer(ComputerName: String; Timeout: Cardinal): Integer;
var
 hIP         : THandle;
 PingBuffer  : array [0 .. 31] of Char;
 pIPe        : ^ICMPEchoReply;
 PHostEn     : PHostEnt;
 LWSAData    : WSAData;
 DestAddress : In_Addr;
 WVersionRequested : WORD;
begin
 Result:= -1;
 hIP:= ICMPCreateFile;
 GetMem(pIPe, SizeOf(ICMPEchoReply) + SizeOf(PingBuffer));
 pIPe.Data         := @PingBuffer;
 pIPe.DataSize     := SizeOf(PingBuffer);
 WVersionRequested := MakeWord(1, 1);
 try
   if WSAStartup(WVersionRequested, LWSAData) = 0 then
   begin
     PHostEn:= GetHostByName(PChar(ComputerName));
     if GetLastError = 0 then
     begin
       DestAddress:= PInAddr(PHostEn^.H_Addr_List^)^;
       IcmpSendEcho(hIP, DestAddress.S_Addr, @PingBuffer,
SizeOf(PingBuffer), nil, pIPe, SizeOf(ICMPEchoReply) + SizeOf(PingBuffer),
Timeout);
       if GetLastError = 0 then Result:= pIPe^.Status;
     end;
     WSACleanup;
   end;
 finally
   ICMPCloseHandle(hIP);
   FreeMem(pIPe);
 end;
end;

function ComputerEnabled(ComputerName: String): Boolean;
begin
 Result:= PingComputer(ComputerName, 500) = IP_SUCCESS;
end;

procedure GetShares(ComputerName: String; var SharesList:
TShareInfoDynArray; ShowHidden: Boolean);
var
 ShareInfo1   : PShareInfo1Array;
 EntriesRead  : DWORD;
 TotalEntries : DWORD;
 CompName     : PWChar;
 TotalSharesList : TShareInfoDynArray;
 i : Integer;
begin
 StrToPWChar(ComputerName, CompName, 64);

 if NetShareEnum(CompName, 1, @ShareInfo1, DWORD(-1), @EntriesRead,
@TotalEntries, nil) = 0 then
   for i:= 1 to EntriesRead do
     if (ShareInfo1^[i].SHI1_Type = SHARETYPE_DISKTREE) and
        (ShowHidden or
(String(ShareInfo1^[i].SHI1_NetName)[Length(String(ShareInfo1^[i].SHI1_NetName)) ]
<> "$")) then
     begin
       SetLength(TotalSharesList, Length(TotalSharesList) + 1);
       TotalSharesList[Length(TotalSharesList) - 1].Name:=
String(ShareInfo1^[i].SHI1_NetName);
       TotalSharesList[Length(TotalSharesList) - 1].Path:= "";
     end;

 FreeMem(CompName);
 SharesList:= Copy(TotalSharesList);
 TotalSharesList:= nil;
end;

procedure GetSharesFromIPRange(IPFrom, IPTo: TIPAddress; var
SharedComputers: TSharedComputerDynArray; var EmptyComputers:
TIPAddressDynArray; ShowHidden: Boolean);
var
 ScanThreads : array of TThreadScanResources;
 TotalSharedComputers : TSharedComputerDynArray;
 TotalEmptyComputers  : TIPAddressDynArray;
 i: Integer;
begin
 if (IPFrom[1] = IPTo[1]) and (IPFrom[2] = IPTo[2]) and (IPFrom[3] =
IPTo[3]) and (IPFrom[4] <= IPTo[4]) then
 begin
   for i:= IPFrom[4] to IPTo[4] do
   begin
     SetLength(ScanThreads, Length(ScanThreads) + 1);
     ScanThreads[Length(ScanThreads) - 1]:=
TThreadScanResources.Create(True);
     with ScanThreads[Length(ScanThreads) - 1] do
     begin
       Started:= False;
       IPAddress:= FullIP(IPFrom[1], IPFrom[2], IPFrom[3], i);
       ShowHiddenShares:= ShowHidden;
       SetLength(SharesList, 0);
       Priority:= tpLowest;
       Resume;
     end;
   end;

   for i:= 0 to Length(ScanThreads) - 1 do
     with ScanThreads[i] do
     begin
       while not Started do Delay(100);
       WaitForSingleObject(Handle, INFINITE);

       if Length(SharesList) > 0 then
         begin
           SetLength(TotalSharedComputers, Length(TotalSharedComputers) +
1);
           TotalSharedComputers[Length(TotalSharedComputers) - 1].IPAddress
:= IPAddress;
           TotalSharedComputers[Length(TotalSharedComputers) -
1].SharesList := Copy(SharesList);
         end
       else
         begin
           SetLength(TotalEmptyComputers, Length(TotalEmptyComputers) + 1);
           TotalEmptyComputers[Length(TotalEmptyComputers) - 1]:=
IPAddress;
         end;

       FreeAndNil(ScanThreads[i]);
       Application.ProcessMessages;
     end;

   SharedComputers := Copy(TotalSharedComputers);
   EmptyComputers  := Copy(TotalEmptyComputers);
   TotalSharedComputers := nil;
   TotalEmptyComputers  := nil;
 end;
end;

procedure TThreadScanResources.Execute;
begin
 Started:= True;
 if ComputerEnabled(IPToStr(IPAddress)) then GetShares(IPToStr(IPAddress),
SharesList, ShowHiddenShares);
end;

{ - - Пример использования - -}

procedure TForm1.Button1Click(Sender: TObject);
var
 SharedComputers : TSharedComputerDynArray;
 EmptyComputers  : TIPAddressDynArray;
 n, p: Integer;
begin
 GetSharesFromIPRange(FullIP(192, 168, 1, 1), FullIP(192, 168, 1, 254),
SharedComputers, EmptyComputers, False);

 Memo1.Clear;
 Memo1.Lines.Add(TimeToStr(GetTime));
 for n:= 0 to Length(SharedComputers) - 1 do
   for p:= 0 to Length(SharedComputers[n].SharesList) - 1 do
     Memo1.Lines.Add(FullNetSharePath(IPToStr(SharedComputers[n].IPAddress),
SharedComputers[n].SharesList[p].Name));
 Memo1.Lines.Add("");
 Memo1.Lines.Add("EmptyComputers = " + IntToStr(Length(EmptyComputers)));

 SharedComputers := nil;
 EmptyComputers  := nil;
end;

{ - - - }

end.


 
Slym ©   (2009-01-30 17:48) [3]

говнокод... на рефакторинг!
первая ошибка - один IP, один поток - нафега?
должно быть: N потоков (ну никак не 255 как у тебя!), буфер из M айпишников...
Поток берет из буфера IP ищет шары и снова ныряет в буфер за новым ипишником
procedure TThreadScanResources.Execute;
begin
while not terminated do
 begin
   if not GetIP(IP) then exit;
   if ComputerEnabled(IPToStr(IPAddress)) then
   begin
     GetShares(IPToStr(IPAddress),SharesList, ShowHiddenShares);
     SetIPShares(IP,SharesList);
   end;
 end;

end


 
Josh ©   (2009-01-30 19:56) [4]

Большое спасибо! Я очень люблю и уважаю критику, особенно с примерами!
Уже переделываю код...


 
Josh ©   (2009-02-01 00:00) [5]

Slym, еще раз спасибо! При уменьшении количества потоков до 5:

1. Время сканирования одного сегмента сети увеличилось в среднем до 0,5 - 1 мин.

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

3. Исчезла утечка памяти! :)


 
Slym ©   (2009-02-02 04:51) [6]

колво потоков можно смело поднять до 8-10
Исчезла утечка памяти возможно и нет :) а снизилось ее проявление в 255/5 раз :)


 
FireMan_Alexey   (2009-02-04 01:13) [7]

procedure TForm1.Button1Click(Sender: TObject);
var
SharedComputers : TSharedComputerDynArray;
EmptyComputers  : TIPAddressDynArray;
n, p: Integer;
begin
GetSharesFromIPRange(FullIP(192, 168, 1, 1), FullIP(192, 168, 1, 254),
SharedComputers, EmptyComputers, False);

Memo1.Clear;
Memo1.Lines.Add(TimeToStr(GetTime));
for n:= 0 to Length(SharedComputers) - 1 do
  for p:= 0 to Length(SharedComputers[n].SharesList) - 1 do
    Memo1.Lines.Add(FullNetSharePath(IPToStr(SharedComputers[n].IPAddress),
SharedComputers[n].SharesList[p].Name));
Memo1.Lines.Add("");
Memo1.Lines.Add("EmptyComputers = " + IntToStr(Length(EmptyComputers)));

SharedComputers := nil;
EmptyComputers  := nil;

end

С динамическим массивом надо SetLength(???,0);
Я один раз забыл, так у меня на 8 Мег утекло :)


 
FireMan_Alexey   (2009-02-04 01:17) [8]

Length(SharedComputers[n].SharesList) - 1 = High(SharedComputers[n].SharesList) на пару шагов меньше :)



Страницы: 1 вся ветка

Форум: "Сети";
Текущий архив: 2011.01.16;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.5 MB
Время: 0.004 c
2-1287741449
kate158
2010-10-22 13:57
2011.01.16
парсер эксель файлов-хочу вытащить дату!


2-1288091783
Ann007
2010-10-26 15:16
2011.01.16
PageСontrol


15-1285492613
Lamer@fools.ua
2010-09-26 13:16
2011.01.16


2-1287920088
George
2010-10-24 15:34
2011.01.16
TABOrder и фреймы


15-1285878556
Юрий
2010-10-01 00:29
2011.01.16
С днем рождения ! 1 октября 2010 пятница





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский