Конференция "Сети" » Глюк компонента TIdUDPServer в 10м Indy? Проверьте ктонибудь. [D7, WinXP]
 
  • sniknik © (14.09.07 23:48) [0]
    Сделал небольшие изменения в старой, рабочей проге, с использованием 9х Indy, а с того времени уже поставил 10е... и словил глюк...
    Вот интересно это только у меня так, возможно я чтото не учитываю (новые настройки например), или это глюк самого Indy?

    Код проги приводить не буду, вместо этого выделил глюк в отдельный тест-пример (ниже), все задано в рантайме, и в одном месте, чтобы было нагляднее, на самом деле это в разных программах (и клиентская часть не моя), интересует серверная часть.

    unit Unit1;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer, IdGlobal,
     IdSocketHandle, StdCtrls, IdUDPClient;

    type
     TForm1 = class(TForm)
       Memo1: TMemo;
       Button1: TButton;
       Button2: TButton;
       procedure FormCreate(Sender: TObject);
       procedure Button1Click(Sender: TObject);
       procedure Button2Click(Sender: TObject);
     private
       i1, i2: integer;
       UDPServer: TIdUDPServer;
       UDPClient: TIdUDPClient;
       procedure UDPRead(Sender: TObject; AData: TBytes; ABinding: TIdSocketHandle);
     public
     end;

    var
     Form1: TForm1;

    implementation

    {$R *.dfm}

    procedure TForm1.UDPRead(Sender: TObject; AData: TBytes; ABinding: TIdSocketHandle);
    begin
     Memo1.Lines.Add(PChar(AData));
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
     UDPServer:= TIdUDPServer.Create(self);
     with UDPServer.Bindings.Add do begin
       IP  := '0.0.0.0';
       Port:= 2500;
     end;
     with UDPServer.Bindings.Add do begin
       IP  := '0.0.0.0';
       Port:= 2501;
     end;
     UDPServer.OnUDPRead:= UDPRead;
     UDPServer.Active:= true;

     UDPClient:= TIdUDPClient.Create(self);
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    begin
     Inc(i1);
     UDPClient.Send('127.0.0.1', 2500, '2500 '+IntToStr(i1));
    end;

    procedure TForm1.Button2Click(Sender: TObject);
    begin
     Inc(i2);
     UDPClient.Send('127.0.0.1', 2501, '2501 '+IntToStr(i2));
    end;

    end.



    Как видно для прослушки устанавливаются 2 порта (минимум для глюка, больше будет тоже, а 1 нормально работает).
    Выражается в том, что сервер не получает второе сообщение если если посылать два и более подряд на один порт. Т.е. его "клинит", "расклинивается" нажатием на другую кнопку (посылкой на другой порт) но тогда начинает задваиваться с первым... но через какоето время если жать опять только один и его "клинит".

    В общем потыкайте в кнопки, в приведенной программке, адекватное поведение получается?
    (indy должен быть 10, в 9ке это точно работало)
    И можно ли это исправить? (В смысле сделать нормально, а не "залечкой" типа вместо одного 2(/3...n) компонента поставить с одним портом)
  • Сергей М. © (16.09.07 11:12) [1]
    Не знаю как в 10-ке, но и в 9-ке этот код нормально работать не должен, ибо обрабортчик OnUDPRead в 9-ке вызывается в дополнительном потоке.
  • sniknik © (16.09.07 21:55) [2]
    ThreadedEvent  по умолчанию false (не думаю что в 9ке отличается), и как видно в коде он не меняется, а обработчик OnUDPRead вызывается в UDPRead так
    procedure TIdUDPListenerThread.Run;
    ...
         if FServer.ThreadedEvent then begin
           UDPRead;
         end else begin
           Synchronize(UDPRead);
         end;
    ...


    Т.е. работать должен. (хотя это и не рабочий код а пример где убрано все для выделения глюка)
    В 9-ке, посмотрел файл, данные строки те же

    Но вообще это отступление от темы, не нравится игнорирование потока измени так
    const
     WM_MEMO_ADD = WM_USER+101;

     ....
     procedure MemoAdd(var Mess: TMessage); message WM_MEMO_ADD;
     ....

    procedure TForm1.MemoAdd(var Mess: TMessage);
    var
     Buf: PChar;
    begin
     Buf:= Pointer(Mess.WParam);
     Memo1.Lines.Add(Buf);
     FreeMem(Buf);
    end;

    procedure TForm1.UDPRead(Sender: TObject; AData: TBytes; ABinding: TIdSocketHandle);
    var
     Ln: integer;
     Buf: PChar;
    begin
     Ln:= Length(AData);
     GetMem(Buf, Ln);
     Move(AData[0], Buf^, Ln);
     Buf[Ln]:= #0;
     PostMessage(Handle, WM_MEMO_ADD, integer(Buf), 0);
    end;



    Это ничего(в смысле описанного глюка) не меняет.
  • sniknik © (16.09.07 21:59) [3]
    Сорри, надо заменить
    GetMem(Buf, Ln+1);

  • Сергей М. © (17.09.07 08:34) [4]

    > начинает задваиваться


    Это как понимать ?
  • sniknik © (17.09.07 09:04) [5]
    > Это как понимать ?
    Код модуля с ошибкой выложен весь, проше всего скомпилить в программу и посмотреть... (в общем то результат такого "посмотреть" мне и нужен, убедиться, что это не только у меня так)

    Результат работы программы
    2500 1  //первой нажатие на первую кнопку, несколько последующих до "клина" нет реакции

    2501 1 //нажатие на вторую кнопку
    2500 2


    2501 2 //еще раз на нее же
    2500 3

    2501 3 //еще, и тд.
    2500 4


    2501 4
    2500 5

    2501 5 //"клин" второй кнопки
    2500 6


    2501 6 //меняем кнопку на первую, продолжается тоже самое для нее до ее "клина"
    2500 7
    2501 7
    2500 8
    2501 8
    2500 9
    2501 9
    2500 10
    2501 10
    2500 11
    2501 11
    2500 12
    2501 12
    2500 13
    2501 13
    2500 14

    "клин" кнопки, это не то что она вдруг "задизейблилась", это то, что сервер почемуто не отдает принятое сразу. Т.к. очевидно что пакет всетаки получил, а отдает порциями вместе с пакетом второго порта.
  • Сергей М. © (17.09.07 10:07) [6]

    > sniknik ©   (17.09.07 09:04) [5]


    Попробовать не могу, у меня 9-ка ..
  • umbra © (17.09.07 10:50) [7]
    если нажимать кнопки по очереди, все работает как ожидается, если нет - как описано в сабже.
  • sniknik © (17.09.07 11:20) [8]
    > Попробовать не могу, у меня 9-ка ..
    в 9-ке работало. но не работал протокол https (в другой прграмме), может не совсем не работал, но какие то проблемы были, не помню, что и заставило поставить 10й (https сразу заработал), а это вот "выплыло" при каком то мелком исправлении (в caption лейбела орфографическую ошибку сделал ;)) и перекомпиляции (хорошо не отослал замену, сначала проверил и заметил... пришлось в старом ресхакером строку поменять и отослать, но это временно, а вдруг еще что понадобиться поменять, где ресхакером не обойдешся?).

    umbra ©   (17.09.07 10:50) [7]
    т.е. у тебя также? ок.
    "по очереди" пакеты не приходят увы. ;(

    ладно, будет время придётся в исходниках "поковыряться".
  • Slym © (17.09.07 12:52) [9]
    sniknik ©   (17.09.07 11:20) [8]
    не проще 2 сервер сокета иметь?
  • umbra © (17.09.07 13:47) [10]

    > не проще 2 сервер сокета иметь?
    >

    похоже, именно в этом и дело. Из исходников следует, что для UDP сервера создается 1 слушающий поток, который в цикле проверяет все привязанные адреса и порты, вызывая на каждом RecvFrom. Если есть две привязки и на первую приходит пакет (нажата Button1), то сведения о нем попадают в мемо, а затем слушающий сокет блокируется на второй привязке и остается блокированным до нажатия второй кнопки. Если сначала нажать Button2, то в мемо ничего не появится до нажатия Button1, т.к. слушающий сокет блокирован на первой привязке. В 9-й инди, скорее всего, на каждую привязку создавался отдельный поток. Почему в 10-й сделали иначе - неясно.
  • sniknik © (17.09.07 15:26) [11]
    > не проще 2 сервер сокета иметь?
    Ну их вообще то не 2 бывает, а ~ от 1 до 30. Не знаю, реализовать массив то просто, но вот будет ли это правильно... (теперь после [10], если там действительно 1 поток на все, слоняюсь к мысли что так правильней т.к., имхо, должен быть поток на каждую прослушку)
  • Сергей М. © (17.09.07 15:31) [12]

    > должен быть поток на каждую прослушку


    Необязательно.
    Можно организовать и в одном потоке с использованием таймаута в select'е.
    Но вот почему индейцы это у себя не организовали (коль скоро там один-единственный слушающий поток) - то для меня загадка.
  • umbra © (17.09.07 16:26) [13]

    > Можно организовать и в одном потоке с использованием таймаута
    > в select'е.

    Я присмотрелся, и таки да, есть там
    Select(0, AReadSet, nil, nil, ATimeout)

    . Тогда вопрос - почему это не срабатывает? И почему срабатывало в 9-й?
  • Сергей М. © (17.09.07 16:31) [14]

    > почему это не срабатывает?


    Надо смотреть код листенер-треда ..

    Похоже что параметр ATimeout некорректен, точнее его значение при вызове берется не то что нужно или не оттуда откуда нужно ..
  • DVM © (17.09.07 16:51) [15]

    > Сергей М. ©   (16.09.07 11:12) [1]
    > Не знаю как в 10-ке, но и в 9-ке этот код нормально работать
    > не должен, ибо обрабортчик OnUDPRead в 9-ке вызывается в
    > дополнительном потоке.

    С чего вы взяли, что в дополнительном?
  • Сергей М. © (17.09.07 16:57) [16]

    > DVM ©   (17.09.07 16:51) [15]


    На сию мысль навело название класса TIdListenerThread.
  • umbra © (18.09.07 10:52) [17]
    нашел засаду!

    в модуле
    IdUDPServer.pas

    есть метод
    TIdUDPListenerThread.Run

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

    procedure TIdUDPListenerThread.Run;
    var
     PeerIP: string;
     i, PeerPort, ByteCount: Integer;
     ReadableFDSet: TIdSocketList;
    begin
      ReadableFDSet := nil;  
     try
     // FReadList.SelectRead(AcceptWait);
     if FReadList.SelectReadList(ReadableFDSet, AcceptWait) then
    //    for i := 0 to FReadList.Count - 1 do try
       for i := 0 to ReadableFDSet.Count - 1 do try
         // Doublecheck to see if we've been stopped    {Do not Localize}
         // Depending on timing - may not reach here if it is in ancestor run when thread is stopped
         if not Stopped then begin
    //        FIncomingData := //FServer.Bindings.BindingByHandle(TIdStackSocketHandle(ReadableFDSet[i]));
           FIncomingData := FServer.Bindings.BindingByHandle(TIdStackSocketHandle(ReadableFDSet[i]));
           SetLength(FBuffer,FBufferSize);
           ByteCount := GStack.ReceiveFrom(FIncomingData.Handle,FBuffer,PeerIP,PeerPort,FIncomingData.IP Version );
           SetLength(FBuffer,ByteCount);
           FIncomingData.SetPeer(PeerIP, PeerPort,FIncomingData.IPVersion);
           if FServer.ThreadedEvent then begin
             UDPRead;
           end else begin
             Synchronize(UDPRead);
           end;
         end;
       except
         // exceptions should be ignored so that other clients can be served in case of a DOS attack
         on E : Exception do
           begin
             FCurrentException := E.Message;
             FCurrentExceptionClass := E.ClassType;
             if FServer.ThreadedEvent then begin
               UDPException;
             end else begin
               Synchronize(UDPException);
           end;
         end;
       end;
     finally
       ReadableFDSet.Free;
     end;
    end;

  • umbra © (18.09.07 10:54) [18]
    правда смотрел последний снэпшот инди - там в
    TIdUDPServer

    для каждой привязк создается отдельный поток. Чем-то их
    select

    не устроил.
  • Сергей М. © (18.09.07 11:14) [19]

    > ReadableFDSet := nil;  


    А эт зачем ?
    Для пущей уверенности что ли ?)
  • umbra © (18.09.07 11:21) [20]

    > А эт зачем ?

    в методе
    SelectReadList(ReadableFDSet, AcceptWait)

    список сокетов создается только если есть готовые к чтению. А поскольку
    Free

    происходт в
    finally

    , то немного уверенности не помешает :)
  • Сергей М. © (18.09.07 11:23) [21]
    А-а-а ..

    ну тады ой)
  • sniknik © (18.09.07 16:17) [22]
    > нашел засаду!
    Спасибо! :о)
    Проверю, и заменю.

    > там в TIdUDPServer для каждой привязк создается отдельный поток. Чем-то их select не устроил.
    чисто интуитивно (объяснить не смогу) мне тоже кажется на каждую нужен отдельный поток... (зря что ли многопроцессорные компы и распараллеливание задач делают? :)
  • Сергей М. © (18.09.07 16:39) [23]

    > sniknik ©   (18.09.07 16:17) [22]


    > зря что ли многопроцессорные компы и распараллеливание задач
    > делают?


    Не зря, думаю ...

    Но, пожалуй, факт есть факт. И я склонен доверять результатам исследований  ув. Umbra (C)
  • sniknik © (18.09.07 23:57) [24]
    Кстати, изменение > umbra ©   (18.09.07 10:52) [17]
    убрало еще одну неприятность, ошибку 10004 (прерванный вызов функции) при завершении программы в режиме разработки... кого это в 10-ке раздражало, пожалуйста готовый рецепт.

    umbra ©   (18.09.07 10:52) [17]
    Спасибо еще раз.
 
Конференция "Сети" » Глюк компонента TIdUDPServer в 10м Indy? Проверьте ктонибудь. [D7, WinXP]
Есть новые Нет новых   [134431   +6][b:0][p:0.003]