Форум: "Сети";
Текущий архив: 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