Модераторы: Snowy, Poseidon, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Один IdTCPServer, много IdTCPCliet-ов и прокси 
:(
    Опции темы
_Rin
Дата 29.6.2007, 14:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 12.9.2006

Репутация: нет
Всего: нет



Имеется сервер и клиент на Indy.
На сервере создан поток (CommonThread), который занимается обработкой комманд от клиентов и рассылкой результатов обработки клиентам.
Клиент шлет комманды серверу и принимает от него результаты её выполнения.

Проводится следующее тестирование: 
1) запускается сервер.
2) через прокси к нему поключаются клиенты (около 100 и больше), и начинают слать комманды (клиенты запускаются с одной машиины)
3) подключение происходит нормально, на сервере каждому клиенту IdTCPServer выделяет свой поток, CommonThread занимается обработкой комманд и рассылкой результатов. Тут все путем
4) Но при последующем массовом отключении клиентов - на сервере не уничтожаются потоки, которые выделялись данным клиентам.... В приложение сервера может остаться несколько десятков незавершенных потоков.

Как правильно завершать потоки клиентов на сервере ?  smile 

Код

//Отключение клиентов
procedure TServerDataModule.IdTCPServerDisconnect(AThread: TIdPeerThread);
begin

 try

   if AThread.Data<>nil then
   begin

        TConnection(AThread.Data).Connected:=false;
        ConnectionList.Remove(TConnection(AThread.Data));
        TConnection(AThread.Data).Free;
        AThread.Data:=nil;

   end;

 except
 end;
 
end;



Код

//Поток чтения данных от клиента
procedure TServerDataModule.IdTCPServerExecute(AThread: TIdPeerThread);
var
 TempStream: TMemoryStream;
 TempStringList: TStringList;
 Command: TCommand;
 i: integer;
begin

 TempStream:=TMemoryStream.Create;
 TempStringList:=TStringList.Create;

 try
   try

     if not AThread.Terminated and AThread.Connection.Connected then
     begin
        Command:=TCommand.Create;

        AThread.Connection.ReadStream(TempStream);
        TConnection(AThread.Data).PassTime:=Time;

        TempStream.Position:=0;
        TempStringList.LoadFromStream(TempStream);

        Command.Command:=TempStringList.Strings[0];
        Command.Connection:=TConnection(AThread.Data);
        CommandList.Add(Command);
      end
      else
        TConnection(AThread.Data).Connected:=false;

   finally
      TempStream.Free;
      TempStringList.Free;
   end;

 except
   on E: Exception do
   begin
      RichEditText:='IdTCPServerExecute: '+AThread.Connection.Socket.Binding.PeerIP+': '+E.Message;
      AThread.Synchronize(ServerForm.PrintToMainRichEdit);
   end;
 end;

end;



CommandList: TThreadList - список комманд, обрабатывается в CommondThread
ConnectionList: TThreadList - список поключенных клиентов,  в CommondThread на основании данного списка производится рассылка сообщений клиентам

PM MAIL   Вверх
Snowy
Дата 29.6.2007, 14:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 53
Всего: 484



Тут дело не в прокси.
Потоки сами отвалятся со временем.
Время зависит только от винды.
Если хочется процесс ускорить, то нужно ввести понятие KeepAlive.
Если клиент какое-то время ничего не шлёт, сервер его отрубает.
Клиент должен или слать периодически незначащие запросы (KeepAlive), если не хочит, чтоб его отстрелили,
либо при отстреле заново отключаться.
Для этого каждый тред на сервере должен считать время без запросов.
Это обычная ситуация для TCP/IP протокола.
PM MAIL   Вверх
_Rin
Дата 3.7.2007, 10:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 12.9.2006

Репутация: нет
Всего: нет



Сделала следующую проверку на сервере: 
- фиксируется время прихода поледнего сообщения от клиента.
- это время периодически сравнивается с текущим временем.
- если разница составляет более некоторого значения (пока 30 секунд), то на сервере происходит отключение данного клиента (делаю Disconnect).

Вроде все работает как надо... smile 

Но при массовом выключении клиентов иногда проиходит зависание одного-двух клиентов (допустим к серверу подключены 150 клиентов, вырубаем всех - 148 выходят нормально, а 2 продолжают висеть в памяти). На сервере при этом может оставаться как раз 2 незавершенных потока. Если убить зависшие клиенты с помощью Windows Task Manager, то на сервере соответсвующие потоки тоже завершаться, при этом возникает исключение: Socket Error # 10054 Connection reset by peer.

Что это значит? 

P.S. тестировние производится на одном компе (и сервер и 150 клиентов работают на одной машине).
PM MAIL   Вверх
Snowy
Дата 3.7.2007, 11:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 53
Всего: 484



Это означает, что была попытка чтения из сокета, а клиент отвалился.
Все операции чтения сокета нужно защищать через try .. except
PM MAIL   Вверх
_Rin
  Дата 3.7.2007, 13:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 12.9.2006

Репутация: нет
Всего: нет



Провела тестирование через прокси сервер....результаты не утешительные - потоки остаются в памяти, и не завершаются (если клиентов меньше 70 - все нормально, 70 и больше - виснут).

Код

  //свои типы данных
  TConnection = class(TObject)//данные о подключенных клиентах
  public
    Buffer: TStringList;
    AThread: TIdPeerThread;
    PassTime: TDateTime;
    SendData: boolean;
    TData: TObject;
    constructor Create;
    destructor Destroy; override;
  end;

  TCommand = class (TObject)//комманда, для буфера комманд
  public
    Command: string;
    Connection: TConnection;
  end;


есть список подключений и список принятый от клиентов комманд
Код

CommandList: TThreadList;//состоит из TCommand
ConnectionList: TThreadList;//состоит из TConnection


Код

//поток обработки комманд, рассылки результатов клиентам 
//тут же проверяется время последнего сообщения от клиента

procedure TCommonThread.Execute;
var
  Command: TCommand;
  Connection: TConnection;
  i: integer;
  TempStream: TMemoryStream;
  TempList: TList;
begin

 TempStream:=TMemoryStream.Create;

 while not Terminated do
 begin
 try


   CurrentTime:=Time;
   //проверка на время
   if MilliSecondSpan(CurrentTime, LastTime)>=CommonThreadTimeInterval then
   begin
          LastTime:=CurrentTime;

          RichEditText:='CommonThread begin check for connections...';
          Synchronize(ServerForm.PrintToMainRichEdit);

           with ConnectionList.LockList do
           try
             for i:=0 to Count-1 do
             begin
               CurrentTime:=Time;
               if (MilliSecondSpan(CurrentTime, TConnection(Items[i]).PassTime)>KeepAlive) then
               begin
                   Remove(Items[i]);
                   TConnection(Items[i]).SendData:=false;
                   if TConnection(Items[i]).AThread.Connection.Connected then
                     TConnection(Items[i]).AThread.Connection.Disconnect;
               end
             end;
           finally
             ConnectionList.UnlockList;
           end;

         RichEditText:='CommonThread ends check for connections...';
         Synchronize(ServerForm.PrintToMainRichEdit);
   end;


   Command:=nil;
   Connection:=nil;
   try

      with CommandList.LockList do 
      try
          ConnectionList.LockList;

          if Count>0 then
          begin
             Command:=TCommand(Items[0]);
             Connection:=Command.Connection;
             Remove(Command);

                if Connection.SendData then
                with Connection.Buffer do
                case CommandDetermination(Trim(Command.Command))[1] of 
                  //тут обработка комманды, и запись результатов Buffer (и другим клиентам тоже)
                end
                else
                   Connection.Buffer.Clear;

             Command.Free;
          end;
      finally
          ConnectionList.UnlockList;
          CommandList.UnlockList;
      end;

   except
      on E: Exception do
      begin
            RichEditText:='??? CommandList: '+E.Message;
            Synchronize(ServerForm.PrintToMainRichEdit);
      end;
   end;



   try
      TempList:=ConnectionList.LockList;
      with TempList do
      try
              //сначала результат посылается тому клиенту, от кого была данная команда             
               if (Connection<>nil) and (Connection.SendData) then
                SendData(Connection, TempStream, TempList);

              //рассылка результатов другим клиентам
              for i:=0 to Count-1 do
              begin
                  if Assigned(TConnection(Items[i])) and
                      (TConnection(Items[i]).SendData) and
                        (TConnection(Items[i])<>Connection) then
                   SendData(TConnection(Items[i]), TempStream,  TempList);
              end;
      finally
          Pack;
          ConnectionList.UnlockList
      end;

   except
     on E: Exception do
     begin
            RichEditText:='!!! ConnectionList: '+E.Message;
            Synchronize(ServerForm.PrintToMainRichEdit);
     end;
   end;



 except
   on E: Exception do
   begin
        RichEditText:='&&& CommonThread: '+E.Message;
        Synchronize(ServerForm.PrintToMainRichEdit);
   end;
 end;
 end;//while not Terminate

 TempStream.Free;

end;



Код

//чтение комманд от клиентов
procedure TServerDataModule.IdTCPServerExecute(AThread: TIdPeerThread);
var
 TempStream: TMemoryStream;
 TempStringList: TStringList;
 Command: TCommand;
 i: integer;
begin

 TempStream:=TMemoryStream.Create;
 TempStringList:=TStringList.Create;

 try
   try

     if not AThread.Terminated and AThread.Connection.Connected and TConnection(AThread.Data).SendData then
     begin
        Command:=TCommand.Create;

        AThread.Connection.ReadStream(TempStream);
        TConnection(AThread.Data).PassTime:=Time;

        TempStream.Position:=0;
        TempStringList.LoadFromStream(TempStream);

        Command.Command:=TempStringList.Strings[0];
        Command.Connection:=TConnection(AThread.Data);
        CommandList.Add(Command);
      end
      else
        TConnection(AThread.Data).SendData:=false;

   finally
      TempStream.Free;
      TempStringList.Free;
   end;
 except
   on E: Exception do
   begin
      RichEditText:='IdTCPServerExecute: '+AThread.Connection.Socket.Binding.PeerIP+': '+E.Message;
      AThread.Synchronize(ServerForm.PrintToMainRichEdit);

      if AThread.Connection.Connected then
        AThread.Connection.Disconnect;
   end;
 end;

end;




Код

//отключение клиента
procedure TServerDataModule.IdTCPServerDisconnect(AThread: TIdPeerThread);
begin

 try
   if AThread.Data<>nil then
   begin
        TConnection(AThread.Data).SendData:=false;
        ConnectionList.Remove(TConnection(AThread.Data));
        TConnection(AThread.Data).Free;
        AThread.Data:=nil;
   end;
 except
 end;
 
end;


В чем может быть дело? Может дело не на стороне сервера, а не стороне клиента?

PM MAIL   Вверх
Rohoss
Дата 4.7.2007, 01:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Начальник интернета
***


Профиль
Группа: Завсегдатай
Сообщений: 1308
Регистрация: 9.10.2006
Где: Matrix

Репутация: нет
Всего: 18



Когда больше ровно 70? В смысле не 69 не 71 а именно 70?


--------------------
Файловый менеджер Explorer.Net скачать  video
PM ICQ   Вверх
_Rin
Дата 4.7.2007, 07:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 48
Регистрация: 12.9.2006

Репутация: нет
Всего: нет



Нет.
Если запускается и подключается к серверу 70 или менее клиентов - то при массовом отключениии - на сервере все их потоки уничтожаются.

Если запускается и подключается к серверу более 70 клиентов - то при массовом отключениии - на сервере остаются некоторые потоки.
PM MAIL   Вверх
Snowy
Дата 4.7.2007, 11:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 11363
Регистрация: 13.10.2004
Где: Питер

Репутация: 53
Всего: 484



Слишком много взаимных блокировок.
Не нужно работать из потока одного сокета с сокетом другого потока.
А уж тем более делать для этого тотальные блокировки.
Вот и получается, что у тебя один повисший тред блокирует всех остальных.
На сервере должно быть минимум блокировок и синхронизаций.
Каждый тред должен работать исключительно со своим сокетом.
Если нужно отправить что-то на другой сокет - ставь задачу треду, чей сокет.
Тред не должен трогать чужие сокеты.
В идеале должен быть один центральный тред, которому сдают принятое и который ставит данные тредам на отправку.
Остальные треды друг с другом вообще не должны взаимодействовать - только через этот основной центр.
А у тебя постоянные перекрёстные блокировки и юзание чужих сокетов.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делится вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0965 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.