Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2018.12.23;
Скачать: CL | DM;

Вниз

Out of memory при работе приложения   Найти похожие ветки 

 
Maloj2007 ©   (2010-10-12 14:59) [0]

Доброго всем времени суток. Возникла следующая проблема, пишу приложение с использованием WinAPI функций. Задача приложения следующая: Принимать входящие соединения от клиентов, парсить трафик и перенаправлять соединения на нужный мне порт. Получается что-то вроде порт-маппеда. Количество одновременно работающих соединений 100-1000. После долгих тестов (более суток работы) при попытке соединения приложение начало выдавать ошибку "Out of memory". В самом приложение используются массивы вида array [0..7FFF] of char для чтения данных и передачи. В каждом потоке получается 4 таких массива. Если соединение разрывается, поток соответственно уничтожается. функции GetMem, FreeMem и т.п. не использую. Кто может подсказать в чем может быть проблема?


 
Медвежонок Пятачок ©   (2010-10-12 15:20) [1]

проблема в отсутствии лога по которому было бы видно, что Если соединение разрывается, поток соответственно уничтожается.


 
Maloj2007 ©   (2010-10-12 15:33) [2]

Логи собственно присутствуют. Поток действительно уничтожается. Появилась мысль, что проблема может быть в следующем: указатели на все потоки хранятся в структуре типа TList. Возможно ли, что проблема в ней? Т.к. к серверу за сутки подключений было более 50000, процедуру Pack я не вызывал. При создании потока он автоматически добавляется в структуру, затем удаляется. По логам видно что идентификатор потока только увеличивается, идентификатор берется:

var
Id:Integer;
tunels:Tlist;
...
Id:=integer(tunels.items[I]);

Если  я правильно понимаю, то в памяти постоянно выделяется место под указатель, но при удалении итема, место не высвобождается (а если и высвобождается, то не используется повторно).


 
Сергей М. ©   (2010-10-12 15:47) [3]

50000 * 4 ~ 200 кб

Мелочь.
Ищи не там где светлее, а где лежит.


 
Медвежонок Пятачок ©   (2010-10-12 15:59) [4]

Логи собственно присутствуют.

Ну тогда ты как минимум знаешь на какой операции получается аутовмемори.


 
Maloj2007 ©   (2010-10-12 16:32) [5]


> Ну тогда ты как минимум знаешь на какой операции получается
> аутовмемори

Да, сообщение вылетает при попытке создания нового тунеля.

 TTunel = class (TObject)
   initserversocket,              //Инициализированный Серверный сокет
   serversocket,              //Серверный сокет
   clientsocket : integer;    //клиентский
   curSockEngine : TObject;   //Сокетный движек создавший этот объект (для возврата назад по обьектам)
   ConnectOrErrorEvent : Cardinal;   //эвент возникающий при соединении клиентского потока
   hServerThread, hClientThread  : integer;  //хендлы потоков
   idServerThread, idClientThread : LongWord;  //threadid потоков
   LastTime:TDateTime; //для разъединения по таймауту
 public
   ErrorMessage:String;
   NeedDeInit: boolean;
   TunelWork:boolean;
   MustBeDestroyed : boolean;
   procedure SendAction(action : byte);
   procedure NewAction(action: byte; Caller: TObject);
   procedure EncryptAndSend(Packet:Tpacket; ToServer:Boolean);
 published
   constructor Create(SockEngine : TObject);
   procedure   Run;
   destructor Destroy; override;
 end;


 
Сергей М. ©   (2010-10-12 16:40) [6]


> Maloj2007 ©   (12.10.10 16:32) [5]


Здесь тоже светло, но лежит не здесь.


 
Maloj2007 ©   (2010-10-12 16:49) [7]


> Сергей М. ©   (12.10.10 16:40) [6]

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


 
Сергей М. ©   (2010-10-12 17:04) [8]


> Maloj2007 ©   (12.10.10 16:49) [7]


Ну так в приведенном тобой коде нет ни намека ни на выделение ни на освобождение)


 
Maloj2007 ©   (2010-10-12 17:14) [9]

Кидаю основные функции маппета, т.е. функции которые исполняю.тся потоками.


type
 TCharArray = array[0..$7FFF] of AnsiChar;
 TCharArrayEx = array[0..$FFFE] of AnsiChar; //2х
 PPacket = ^TPacket;  
 TPacket = packed record case Integer of
   0: (Size: Word;
       Data: array[0..$7FFE] of Byte);
   1: (PacketAsByteArray: array[0..$7FFF] of Byte);
   2: (PacketAsCharArray: TCharArray);
   3: (pckSize: Word;
       pckId: Byte;
       pckData: array[0..$7FFE] of Byte);
 end;

 TTunel = class (TObject)
   initserversocket,              //Инициализированный Серверный сокет
   serversocket,              //Серверный сокет
   clientsocket : integer;    //клиентский
   curSockEngine : TObject;   //Сокетный движек создавший этот объект (для возврата назад по обьектам)
   ConnectOrErrorEvent : Cardinal;   //эвент возникающий при соединении клиентского потока
   hServerThread, hClientThread  : integer;  //хендлы потоков
   idServerThread, idClientThread : LongWord;  //threadid потоков
   EncDec:TEncDec;
   LastTime:TDateTime;
 public
   ErrorMessage:String;
   NeedDeInit: boolean;
   TunelWork:boolean;
   MustBeDestroyed : boolean;
   ServerKey:Integer;
   UserKey:Integer;
   IsBot:Boolean;
   procedure SendAction(action : byte);
   procedure NewAction(action: byte; Caller: TObject);
   procedure EncryptAndSend(Packet:Tpacket; ToServer:Boolean);
 published
   constructor Create(SockEngine : TObject);
   procedure   Run;
   destructor Destroy; override;
 end;

 TSocketEngine = class (TObject)
   tunels : TList;
 private
   WSA: TWSAData;
   hServerListenThread  : integer;
   idServerListenThread: Cardinal;
   ServerListenSock: Integer;
   function  WaitClient(var hSocket, NewSocket: TSocket): Boolean;
   function  WaitForData(Socket: TSocket; Timeout: Longint): Boolean;
   procedure DeInitSocket(VAR hSocket : integer; const ExitCode: Integer);
   function  InitSocket(var hSocket: TSocket; Port: Word; IP: String): Boolean;
   function  GetSocketData(Socket: TSocket; var Data; const Size: Word): Boolean;
   function  ConnectToServer(var hSocket: TSocket; Port: Word; IP: Integer): Boolean;
 public
   ErrorMessage:String;
   //Установать перед Init
   ServerPort : Word;
   //можно менять в момент работы
   RedirrectIP : Integer;
   RedirrectPort : Word;
   //установить флаг если надо уничтожить
   isServerTerminating : boolean;
   procedure SendAction(action : byte);
   procedure NewAction(action: byte; Caller: TObject);
 published
   procedure DestroyDeadTunels;
   constructor Create; //создание и предустановка
   Procedure StartServer; //запуск, вызывать после креейта и установки всех проперти
   destructor Destroy; override; //по цепочке разрушит все имеющиеся экземпляры TTunel
 end;


 
Maloj2007 ©   (2010-10-12 17:17) [10]

{=============================Thread=============================}
procedure ServerListen(CurrentEngine: TSocketEngine);
var
 NewSocket: TSocket;
 NewTunel : Ttunel;
begin
 try
   with CurrentEngine do
   begin
     if not InitSocket(ServerListenSock, ServerPort, "0.0.0.0") then
     begin
       SendAction(TSockEngineActionListenErrror);
       exit;
     end;
     SendAction(TSockEngineActionListen);
     while WaitClient(ServerListenSock, NewSocket) do
     begin
       SendAction(TSockEngineActionNewConnect);
       //новое соединение на серверный сокет. создаем тунель.
       NewTunel := TTunel.Create(CurrentEngine);
       NewTunel.serversocket := NewSocket; //айди серверного сокета = наш индефикатор
       NewTunel.initserversocket := NewSocket; //айди серверного сокета = наш индефикатор
       NewTunel.Run; //и запускаем его
     end;
 end;
 except
   Addtolog("Error ServerListen"+IntToStr(GetLastError));
 end;
end;

procedure ServerBody(thisTunel:Ttunel);
var
 StackAccumulator : TCharArrayEx;
 PreAccumulator : TCharArray;
 AccumulatorLen : Cardinal;
 BytesInStack : Longint;
 curPacket,NewPacket : TPacket;
 RecvBytes : Int64;
 PreSize, LastResult : Word;
 EventTimeout : boolean;
 IP: Integer;
 IPb:array[0..3] of Byte absolute ip;
 buff:String;
begin
 try
   with TSocketEngine(thisTunel.curSockEngine) do
   begin
     thisTunel.ConnectOrErrorEvent := CreateEvent(nil, true,false,PChar("ConnectOrErrorEvent"+IntToStr(thisTunel.hServerThread)));
     thisTunel.hClientThread :=BeginThread(nil, 0, @ClientBody, thisTunel, 0, thisTunel.idClientThread);
     thisTunel.SendAction(TTunelActionConnectClient);
     EventTimeout := (WaitForSingleObject(thisTunel.ConnectOrErrorEvent, 30000) <> 0);
     if (EventTimeout) or (not thisTunel.TunelWork) then
     begin
       CloseHandle(thisTunel.ConnectOrErrorEvent);
       thisTunel.MustBeDestroyed := true;
       thisTunel.TunelWork := false;
       DeinitSocket(thisTunel.serversocket,WSAGetLastError);
       TerminateThread(thisTunel.hServerThread,0);
     end;
     CloseHandle(thisTunel.ConnectOrErrorEvent);
     ip := RedirrectIP;
     AccumulatorLen := 0;
     LastResult := 1;
     FillChar(PreAccumulator[0],$7fff,0);
     While (thisTunel.serversocket <> -1) do
     try
       ioctlsocket(thisTunel.serversocket, FIONREAD, BytesInStack);
       if BytesInStack = 0 then
         BytesInStack := 1;
       RecvBytes := recv(thisTunel.serversocket, PreAccumulator[0], BytesInStack, 0);//Читаем 1 байт или весь буффер сразу
       if RecvBytes <= 0 then
         break
       else
         PreSize := RecvBytes;
       LastResult := PreSize;
       if lastresult = 1 then
       begin
         ioctlsocket(thisTunel.serversocket, FIONREAD, BytesInStack);
         if BytesInStack > $7FFE then
           BytesInStack := $7FFE;
         if BytesInStack > 0 then
         begin
           RecvBytes := recv(thisTunel.serversocket, PreAccumulator[presize], BytesInStack, 0);
           if RecvBytes <= 0 then
             break
           else
             LastResult := LastResult + RecvBytes;
         end;
       end;
       if LastResult > 0 then
       begin
         Move(PreAccumulator[0], StackAccumulator[AccumulatorLen], LastResult);
         FillChar(PreAccumulator[0],$7fff,0);
         Inc(AccumulatorLen, LastResult);
     except
       break;
     end;
     try
       thisTunel.sendAction(TTunelActionDisconnectClient);
       DeinitSocket(thisTunel.serversocket,WSAGetLastError);
       if thisTunel.clientsocket <> -1 then
         DeinitSocket(thisTunel.clientsocket,WSAGetLastError);
       thisTunel.TunelWork := false;
       thisTunel.MustBeDestroyed := true;
     except
     end;
   end;
 except
 end;
end;



 
Maloj2007 ©   (2010-10-12 17:17) [11]

Procedure ClientBody(thisTunel:Ttunel);
var
 PreAccumulator : TCharArray;
 StackAccumulator : TCharArrayEx;
 AccumulatorLen : Cardinal;
 curPacket : TPacket;
 BytesInStack : Longint;
 PreSize, LastResult : Word;
 IP: Integer;
 IPb:array[0..3] of Byte absolute ip;
 recvbytes : int64;
begin
 try
   with TSocketEngine(thisTunel.curSockEngine) do
   begin
     if not InitSocket(thisTunel.clientsocket,0,"0.0.0.0") then
     begin
       EndThread(0);
     end;
     ip := RedirrectIP;

     if not ConnectToServer(thisTunel.clientsocket, RedirrectPort, RedirrectIP) then
     begin
       SetEvent(thisTunel.ConnectOrErrorEvent); //разрешаем сдвинутся с места в сервербоди
       EndThread(0);
     end;
     thisTunel.SendAction(TTunelActionConnectServer);
     thisTunel.TunelWork := true;
     SetEvent(thisTunel.ConnectOrErrorEvent); //разрешаем сдвинутся с места в сервербоди
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     AccumulatorLen := 0;
     LastResult := 1;
     While (thisTunel.clientsocket <> -1) do
     begin //Читаем пока не отвалимся
       try
         //Сколько еще в буфере ?!
         ioctlsocket(thisTunel.clientsocket, FIONREAD, BytesInStack);
         if BytesInStack = 0 then
           BytesInStack := 1;
         RecvBytes := recv(thisTunel.clientsocket, PreAccumulator[0], BytesInStack, 0);//Читаем 1 байт или весь буффер сразу
         if RecvBytes <= 0 then
           break
         else
           PreSize := RecvBytes;
         LastResult := PreSize;
         if lastresult = 1 then //Мы ждали данных. поэтому там 1 байт. дочитываем.
         begin
           ioctlsocket(thisTunel.clientsocket, FIONREAD, BytesInStack);
           if BytesInStack > $7FFE then
             BytesInStack := $7FFE; //В прочитаном буффере - не более чем то что можем скушать за раз.
           if BytesInStack > 0 then //Дочитываем
           begin
             RecvBytes := recv(thisTunel.clientsocket, PreAccumulator[presize], BytesInStack, 0);
             if RecvBytes <= 0 then
               break
             else
               LastResult := LastResult + RecvBytes;
           end;
         end;
         if LastResult > 0 then
         begin
           Move(PreAccumulator[0], StackAccumulator[AccumulatorLen], LastResult);
           FillChar(PreAccumulator[0],$7fff,0);
           inc(AccumulatorLen, LastResult);
       except
         AddToLog("2"+IntToStr(GetLastError));
       end;
     end;//While LastResult <> SOCKET_ERROR do
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     //сюда попадаем когда отвалился сервер
     //уведомляем
     thisTunel.SendAction(TTunelActionDisconnectServer);
     //пишем в лог (добиваем сокет)
     DeinitSocket(thisTunel.clientsocket,WSAGetLastError);
     //закрываем серверный сокет
     if thisTunel.clientsocket <> -1 then
       DeinitSocket(thisTunel.serversocket,WSAGetLastError);
     thisTunel.TunelWork := false;
     //ставим этому обьекту статус камикадзе если надо.
     thisTunel.MustBeDestroyed := true;
   end;
 except
   Addtolog("Error ClientBody"+IntToStr(GetLastError));
 end;
end;
{=============================Thread=============================}


 
Сергей М. ©   (2010-10-12 17:40) [12]

> EndThread

Этот вызов запросто м.б. источником утечек.
Пользоваться им нужно с умом, а не абы где.


> одновременно работающих соединений 100-1000


И какждое соединение у тебя обслуживается тредом с мегабайтным стеком каждый.

1 мб * 1000 ~ 1гб

Не мудрено что в ВАП процесса в какой-то момент времени при создании  очередного треда не нашлось свободного региона размером в 1 мб


 
Maloj2007 ©   (2010-10-12 18:15) [13]


> Сергей М. ©   (12.10.10 17:40) [12]

Если я правильно понял тебя, ты предлогаешь уменьшить буфер?

ЗЫ: Система работает на Win Server x64 16 Гб ОП


 
Rouse_ ©   (2010-10-12 19:41) [14]

Возьми профайлер и прогони им - он явно покажет где у тебя закавыка...


 
sniknik ©   (2010-10-12 20:51) [15]

> ЗЫ: Система работает на Win Server x64 16 Гб ОП
32 разрядному приложению это как бы до лампочки... не зря же 32 разрядная XP "видит" не больше 3 гиг. да и то через одно место, а нормально только 2.


 
Сергей М. ©   (2010-10-12 21:33) [16]


> Maloj2007 ©   (12.10.10 18:15) [13]
> предлогаешь уменьшить буфер?


Ни про какой буфер я не говорил.
Я лишь заострил твое внимание на том что каждый твой тред требует выделения ему в ВАП твоего 32-разрядного (!) процесса региона размером 1 мб.
А тредов у тебя, как ты заявил, может одновременно существовать до 1000.
А ВАП - оно не резиновое.
+ дефрагментация.

Так что мотай на ус.

И про EndThread призадумайся.
Exit() придумана не для Пушкина.


 
Maloj2007 ©   (2010-10-13 00:00) [17]


> Сергей М. ©   (12.10.10 21:33) [16]

Спасибо, буду думать =)


 
RGV ©   (2010-10-13 09:38) [18]

IO completion port ?

Порт завершения ввода-вывода (IO completion port, IOCP). Реализованный в ядре ОС и доступный через системные вызовы объект «очередь» с операциями «поместить структуру в хвост очереди» и «взять следующую структуру с головы очереди» — последний вызов приостанавливает исполнение потока в случае, если очередь пуста, и до тех пор, пока другой поток не осуществит вызов «поместить». Главнейшей особенностью IOCP является то, что структуры в него могут помещаться не только явным системным вызовом из режима пользователя, но и неявно внутри ядра ОС как результат завершения асинхронной операции ввода-вывода на одной из дескрипторов файлов. Для достижения такого эффекта необходимо использовать системный вызов «связать дескриптор файла с IOCP». В этом случае помещенная в очередь структура содержит в себе код ошибки операции ввода-вывода, а также, для случая успеха этой операции — число реально введенных или выведенных байт. Реализация порта завершения также ограничивает число потоков, исполняющихся на одном процессоре/ядре после получения структуры из очереди. Объект специфичен для MS Windows, и позволяет обработку входящих запросов соединения и порций данных в серверном программном обеспечении в архитектуре, где число потоков может быть меньше числа клиентов (нет требования создавать отдельный поток с расходами ресурсов на него для каждого нового клиента).

wiki.



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

Текущий архив: 2018.12.23;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.006 c
6-1286881150
Maloj2007
2010-10-12 14:59
2018.12.23
Out of memory при работе приложения


15-1475703002
Юрий
2016-10-06 00:30
2018.12.23
С днем рождения ! 6 октября 2016 четверг


15-1475791274
Германн
2016-10-07 01:01
2018.12.23
Светлая память Анатолию Подгорецкому!


15-1476200879
Кто б сомневался
2016-10-11 18:47
2018.12.23
А есть расширения для хрома для проверки email?


15-1475245332
iop
2016-09-30 17:22
2018.12.23
задачка