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

Вниз

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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.002 c
2-1475106928
Arthur
2016-09-29 02:55
2018.12.23
чтение из файла и запись


2-1474431273
glazkov
2016-09-21 07:14
2018.12.23
TreeView


4-1290160827
Boatswain
2010-11-19 13:00
2018.12.23
Кат узнать флэшка или кард-ридер


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


2-1475012292
Arthur
2016-09-28 00:38
2018.12.23
memo. запись в строку





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский