Форум: "Сети";
Текущий архив: 2008.07.06;
Скачать: [xml.tar.bz2];
ВнизГлюк компонента TIdUDPServer в 10м Indy? Проверьте ктонибудь. Найти похожие ветки
← →
sniknik © (2007-09-14 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) компонента поставить с одним портом)
← →
Сергей М. © (2007-09-16 11:12) [1]Не знаю как в 10-ке, но и в 9-ке этот код нормально работать не должен, ибо обрабортчик OnUDPRead в 9-ке вызывается в дополнительном потоке.
← →
sniknik © (2007-09-16 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 © (2007-09-16 21:59) [3]Сорри, надо заменить
GetMem(Buf, Ln+1);
← →
Сергей М. © (2007-09-17 08:34) [4]
> начинает задваиваться
Это как понимать ?
← →
sniknik © (2007-09-17 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
"клин" кнопки, это не то что она вдруг "задизейблилась", это то, что сервер почемуто не отдает принятое сразу. Т.к. очевидно что пакет всетаки получил, а отдает порциями вместе с пакетом второго порта.
← →
Сергей М. © (2007-09-17 10:07) [6]
> sniknik © (17.09.07 09:04) [5]
Попробовать не могу, у меня 9-ка ..
← →
umbra © (2007-09-17 10:50) [7]если нажимать кнопки по очереди, все работает как ожидается, если нет - как описано в сабже.
← →
sniknik © (2007-09-17 11:20) [8]> Попробовать не могу, у меня 9-ка ..
в 9-ке работало. но не работал протокол https (в другой прграмме), может не совсем не работал, но какие то проблемы были, не помню, что и заставило поставить 10й (https сразу заработал), а это вот "выплыло" при каком то мелком исправлении (в caption лейбела орфографическую ошибку сделал ;)) и перекомпиляции (хорошо не отослал замену, сначала проверил и заметил... пришлось в старом ресхакером строку поменять и отослать, но это временно, а вдруг еще что понадобиться поменять, где ресхакером не обойдешся?).
umbra © (17.09.07 10:50) [7]
т.е. у тебя также? ок.
"по очереди" пакеты не приходят увы. ;(
ладно, будет время придётся в исходниках "поковыряться".
← →
Slym © (2007-09-17 12:52) [9]sniknik © (17.09.07 11:20) [8]
не проще 2 сервер сокета иметь?
← →
umbra © (2007-09-17 13:47) [10]
> не проще 2 сервер сокета иметь?
>
похоже, именно в этом и дело. Из исходников следует, что для UDP сервера создается 1 слушающий поток, который в цикле проверяет все привязанные адреса и порты, вызывая на каждом RecvFrom. Если есть две привязки и на первую приходит пакет (нажата Button1), то сведения о нем попадают в мемо, а затем слушающий сокет блокируется на второй привязке и остается блокированным до нажатия второй кнопки. Если сначала нажать Button2, то в мемо ничего не появится до нажатия Button1, т.к. слушающий сокет блокирован на первой привязке. В 9-й инди, скорее всего, на каждую привязку создавался отдельный поток. Почему в 10-й сделали иначе - неясно.
← →
sniknik © (2007-09-17 15:26) [11]> не проще 2 сервер сокета иметь?
Ну их вообще то не 2 бывает, а ~ от 1 до 30. Не знаю, реализовать массив то просто, но вот будет ли это правильно... (теперь после [10], если там действительно 1 поток на все, слоняюсь к мысли что так правильней т.к., имхо, должен быть поток на каждую прослушку)
← →
Сергей М. © (2007-09-17 15:31) [12]
> должен быть поток на каждую прослушку
Необязательно.
Можно организовать и в одном потоке с использованием таймаута в select"е.
Но вот почему индейцы это у себя не организовали (коль скоро там один-единственный слушающий поток) - то для меня загадка.
← →
umbra © (2007-09-17 16:26) [13]
> Можно организовать и в одном потоке с использованием таймаута
> в select"е.
Я присмотрелся, и таки да, есть тамSelect(0, AReadSet, nil, nil, ATimeout)
. Тогда вопрос - почему это не срабатывает? И почему срабатывало в 9-й?
← →
Сергей М. © (2007-09-17 16:31) [14]
> почему это не срабатывает?
Надо смотреть код листенер-треда ..
Похоже что параметр ATimeout некорректен, точнее его значение при вызове берется не то что нужно или не оттуда откуда нужно ..
← →
DVM © (2007-09-17 16:51) [15]
> Сергей М. © (16.09.07 11:12) [1]
> Не знаю как в 10-ке, но и в 9-ке этот код нормально работать
> не должен, ибо обрабортчик OnUDPRead в 9-ке вызывается в
> дополнительном потоке.
С чего вы взяли, что в дополнительном?
← →
Сергей М. © (2007-09-17 16:57) [16]
> DVM © (17.09.07 16:51) [15]
На сию мысль навело название класса TIdListenerThread.
← →
umbra © (2007-09-18 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 © (2007-09-18 10:54) [18]правда смотрел последний снэпшот инди - там в
TIdUDPServer
для каждой привязк создается отдельный поток. Чем-то ихselect
не устроил.
← →
Сергей М. © (2007-09-18 11:14) [19]
> ReadableFDSet := nil;
А эт зачем ?
Для пущей уверенности что ли ?)
← →
umbra © (2007-09-18 11:21) [20]
> А эт зачем ?
в методеSelectReadList(ReadableFDSet, AcceptWait)
список сокетов создается только если есть готовые к чтению. А посколькуFree
происходт вfinally
, то немного уверенности не помешает :)
← →
Сергей М. © (2007-09-18 11:23) [21]А-а-а ..
ну тады ой)
← →
sniknik © (2007-09-18 16:17) [22]> нашел засаду!
Спасибо! :о)
Проверю, и заменю.
> там в TIdUDPServer для каждой привязк создается отдельный поток. Чем-то их select не устроил.
чисто интуитивно (объяснить не смогу) мне тоже кажется на каждую нужен отдельный поток... (зря что ли многопроцессорные компы и распараллеливание задач делают? :)
← →
Сергей М. © (2007-09-18 16:39) [23]
> sniknik © (18.09.07 16:17) [22]
> зря что ли многопроцессорные компы и распараллеливание задач
> делают?
Не зря, думаю ...
Но, пожалуй, факт есть факт. И я склонен доверять результатам исследований ув. Umbra (C)
← →
sniknik © (2007-09-18 23:57) [24]Кстати, изменение > umbra © (18.09.07 10:52) [17]
убрало еще одну неприятность, ошибку 10004 (прерванный вызов функции) при завершении программы в режиме разработки... кого это в 10-ке раздражало, пожалуйста готовый рецепт.
umbra © (18.09.07 10:52) [17]
Спасибо еще раз.
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2008.07.06;
Скачать: [xml.tar.bz2];
Память: 0.53 MB
Время: 0.052 c