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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Зависание трида с сокетом, многопоточный обмен с использов-ем Indy 
:(
    Опции темы
SilverShield
Дата 16.3.2008, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Приветствую! Уважаемые, помогите пожалуйста советом! Проблема следующая:
Организован обмен между одним компьютером-сервером и множеством компьютеров-клиентов. На компе-сервере (для сокращения ксер) запущена программа с компонентом TIdServer, на компах-клиентах (для сокращения ккл) соответственно TIdClient. Обмен организован так: на ксер делаю TIdClient.Connect(300) и читаю ответ (TIdClient.Readln). Если пришло установленное слово, то передаю ккл запросы (командой TIdClient.Writeln), а он в соответствии с ними присылает нужную информацию. В т.ч. среди запросов к ккл есть запрос на получение файлов. В этом случае на ккл делаю WriteStream, а на стороне ксер делаю ReadStream. В общем случае первым файлом запрашиваю файл-каталог, в котором содержится список файлов, которые имеются на ккл, а затем не закрывая соединение читаю содержимое этого файла и запрашиваю все остальные файлы из этого каталога.
Вся эта схема работает достаточно стабильно при однопоточном режиме обмена, когда использую на ксер компоненты TIdClient созданные в дизайн-тайм. Для оптимизации обмена реализовал многопоточность: создаю несколько тридов, в каждом из которых динамически создаю сокет и произвожу обмен. По завершению обмена вызываю Event "OnComplete", закрываю сокет и разрушаю его. Трид должен разрушаться самостоятельно по завершению своей работы. 
Суть проблемы в следующем: в ряде случаев (не всегда, но часто) поток может не завершиться (не вызывается мой евент OnComplete). Предполагаю что это из-за зависания сокета, т.к. больше в триде нет ничего что могло бы глючить. На какой стадии обмена происходит зависание определить не удалось. Пытался бороть уже по-всякому. Для сокета установлен ReadTimeout. Пытался заводить отдельно таймер, который по своему таймауту будет производить disconnect для сокета. Но пока все безрезультатно.  
Буду чрезмерно благодарен за советы.
Ниже привожу часть кода:
Создание трида

Код

constructor TSurClient.Create(const AClientIP, AClientMAC: string;
      ASurClientCmd: TSurClientCmd; CreateSuspended: Boolean = True);
begin
  inherited Create(CreateSuspended);

  FSurClientCmd := ASurClientCmd;

  FTcpClient := TIdTCPClient.Create(nil);

  ClientIP := AClientIP;
  ClientMAC := AClientMAC;

  FTcpClient.Port := DEFAULT_CLIENT_PORT;
  FTcpClient.ReadTimeout:= 8000;
end;

destructor TSurClient.Destroy;
begin
  if FTcpClient.Connected then
    FTcpClient.Disconnect;
  FreeAndNil(FTcpClient);

  inherited;
end;


Основная часть:

Код

procedure TSurClient.Execute;
var
  res: integer;
  str: string;
begin
  inherited;

  try
      FTcpClient.Host := ClientIP;
      FTcpClient.Connect(300);
      str:= FTcpClient.Readln();
      if str <> 'OK' then
      begin
        FTcpClient.Disconnect;
      end;

      if FTcpClient.Connected then
      begin
      .... основная часть обмена
      
     if FTcpClient.Connected then FTcpClient.Disconnect;
  finally
      if Assigned(OnCompleat) then
      OnCompleat(Self, FErrCode);  
  end;



Получение файлов происходит так:

Код

procedure TSurClient.CommandGetFile(const ARequestCommand, APath: string;
  const ARetry: Byte);
var
  crcRecived, crc: LongWord;
  CRCIsRigth: Boolean;
begin  
  Writeln(ARequestCommand);
  CRCIsRigth := False;
  try
    crcRecived := FTcpClient.ReadCardinal();
    ReadStream(Stream);
    CalculateCRC32Stream(Stream, Stream.Size, crc);
    CRCIsRigth := (crc = crcRecived);
  finally
    if not CRCIsRigth then
      DeleteFiles(APath);
  end;    

  if not CRCIsRigth then // если CRC не верно
  begin
    if (ARetry >= MAX_RETRY_COUNT) then // если все повторы кончились то толкнуть ошибку
      RaiseError(ESurCRCError.Create('Wrong CRC!'))
    else // повторить попытку
      CommandGetFile(ARequestCommand, APath, ARetry + 1);
  end;
end;


PS: AntiFreeze стоит на главной форме приложения. Отдельно в каждом потоке я его не создаю. Хотя такой эксперимент тоже ставил - не помагает. Тем более что зависает то не целиком программа, а только отдельный поток обмена.

PM MAIL   Вверх
MetalFan
Дата 16.3.2008, 12:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Аццкий Сотона
****


Профиль
Группа: Комодератор
Сообщений: 3815
Регистрация: 2.10.2006
Где: Moscow

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



1. зачем антифриз при многопоточной работе инди?! имхо он тут даже может быть вреден.
2. что происходит в OnCompleAt? учитывается ли то, что этот метод вызывается в контексте доп.потока?
3. а вообще выложи тестовый проект, у тебя там видимо куча недочетов, вот что-то и виснет.


--------------------
There are always someone smarter than you...
PM MAIL   Вверх
SilverShield
Дата 17.3.2008, 09:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Прикрепил тестовый проект серверной части (которая производит опрос).  smile 
Очень надеюсь, что у меня там действительно недочеты иначе  smile 

Присоединённый файл ( Кол-во скачиваний: 10 )
Присоединённый файл  kserv.zip 32,50 Kb
PM MAIL   Вверх
dumb
Дата 18.3.2008, 04:51 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

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



Цитата(SilverShield @  16.3.2008,  12:08 Найти цитируемый пост)
На компе-сервере (для сокращения ксер) запущена программа с компонентом TIdServer, на компах-клиентах (для сокращения ккл) соответственно TIdClient. Обмен организован так: на ксер делаю TIdClient.Connect(300) и читаю ответ (TIdClient.Readln)
соединение инициирует IdClient. принимает IdServer. а тут явно какая-то лабуда - ты пишешь, что на сервере лежит IdServer и сервер же делает IdClient.Connect...

в прикрепленном проекте "серверной части" вообще нет упоминания IdServer, и, судя по тому, что там есть, IdServer таки находится в "клиенте". у тебя перепутаны понятия клиента и сервера - в твоем случае один клиент опрашивает много серверов.

так как проект не открывал, то только то, что бросилось в глаза:
"детская болезнь" - обращения к VCL-компонентам из создаваемых потоков.
туда же несинхронизированный доступ к данным.
полная бесполезность употребления RyTimer'а, так как он просто не работает в таких условиях(отсутствие выборки сообщений).
procedure TSurClient.Execute; begin inherited; - мощно, но вроде как безобидно. smile
...

Цитата(SilverShield @  16.3.2008,  12:08 Найти цитируемый пост)
Для оптимизации обмена реализовал многопоточность
в общем, почитай сначала:
Многопоточность - как это делается в Дельфи.
Пример простого многопоточного приложения.


PM MAIL   Вверх
SilverShield
Дата 18.3.2008, 10:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



dumb, да согласен, с понятиями перепутал, но суть дела не меняет )

Цитата(dumb @  18.3.2008,  04:51 Найти цитируемый пост)
"детская болезнь" - обращения к VCL-компонентам из создаваемых потоков.
туда же несинхронизированный доступ к данным.

Ну, я внутри потока обращаюсь только к компонентам, которые созданы внутри этого потока.
Вот объявление класса TSurClient:
Код

TSurClient = class(TAdvThread)
  private
    FTcpClient: TIdTCPClient;
    FClientIP, FClientCompName: string;
    FLogPath: string;
    FTag: Integer;
    FErrCode: Integer;
    TimeOutTimer: TRyTimer;   //таймер, по которому будем делать дисконнект сокета и terminate

    FOnCompleat: TOnCompleatEvent;
    FOnExceptionEvent: TOnExceptionEvent;

    procedure TimerTime(Sender: TObject); // событие об окончании работы таймера

  protected
    procedure CommandGetScrFiles(const AIndexFile: string);
    procedure CommandGetLog(const ARequestCommand, ALogPath: string; const ARetry: Byte = 0);

    procedure Writeln(const AOut: string = '');
    function Readln(): string;
    procedure ReadStream(AStream: TStream);
    function ReadInteger(): Integer;

    procedure ConnectTcpClient;

    procedure Execute; override;
    procedure RaiseError(AException: Exception);

    function GetErrCode(AException: Exception): Integer;

  public
    version: string;
    constructor Create(const AClientCompName, AClientIP: string;
      CreateSuspended: Boolean = True);
    destructor Destroy; override;

    property ClientIP: string read FClientIP write FClientIP;
    property ClientCompName: string read FClientCompName write FClientCompName;

    property Tag: Integer read FTag write FTag;

    property OnCompleat: TOnCompleatEvent read FOnCompleat write FOnCompleat;
    property OnExceptionEvent: TOnExceptionEvent read FOnExceptionEvent
      write FOnExceptionEvent;
  end;

Может я ошибаюсь, но по-моему тут с синхронизацией проблем быть не должно.

Цитата(dumb @  18.3.2008,  04:51 Найти цитируемый пост)
полная бесполезность употребления RyTimer'а, так как он просто не работает в таких условиях(отсутствие выборки сообщений).

Поясните пожалуйста. TRyTimer это не дельфовый таймер, а класс основанный на таймерах винды.

Спасибо за ссылки, но боюсь в данном случае просто принципов оргнизации многопоточности недостаточно. Тут еще сетевой обмен подключается.

PM MAIL   Вверх
dumb
Дата 18.3.2008, 22:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


sceloglauxalbifacies
****


Профиль
Группа: Экс. модератор
Сообщений: 2929
Регистрация: 16.6.2006

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



Цитата(SilverShield @  18.3.2008,  10:32 Найти цитируемый пост)
Может я ошибаюсь, но по-моему тут с синхронизацией проблем быть не должно
речь не о самом классе, а о назначенном обработчике завершения - OnCompleat(кстати, правильно пишется таки Complete).

Цитата(SilverShield @  18.3.2008,  10:32 Найти цитируемый пост)
TRyTimer это не дельфовый таймер, а класс основанный на таймерах винды.

SetTimer:
Цитата(msdn)

An application can process WM_TIMER messages by including a WM_TIMER case statement in the window procedure or by specifying a TimerProc callback function when creating the timer. When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMERTherefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER


что касается таймаутов, то в индейских функциях чтения необязательным параметром можно указать время ожидания ответа, что тебе и нужно, так как только Read* могут висеть в "бесконечном" ожидании до появления данных.
PM MAIL   Вверх
SilverShield
Дата 28.3.2008, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Вроде разобрался )   Были ошибки с синхронизацией доступа в основном потоке. Спасибо за советы )
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Сети"
Snowy
Poseidon
MetalFan

Запрещено:

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

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

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

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

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


 




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


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

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