-
Сделал небольшие изменения в старой, рабочей проге, с использованием 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
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) компонента поставить с одним портом)
-
Не знаю как в 10-ке, но и в 9-ке этот код нормально работать не должен, ибо обрабортчик OnUDPRead в 9-ке вызывается в дополнительном потоке.
-
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; Это ничего(в смысле описанного глюка) не меняет.
-
Сорри, надо заменить GetMem(Buf, Ln+1);
-
> начинает задваиваться
Это как понимать ?
-
> Это как понимать ? Код модуля с ошибкой выложен весь, проше всего скомпилить в программу и посмотреть... (в общем то результат такого "посмотреть" мне и нужен, убедиться, что это не только у меня так)
Результат работы программы 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
"клин" кнопки, это не то что она вдруг "задизейблилась", это то, что сервер почемуто не отдает принятое сразу. Т.к. очевидно что пакет всетаки получил, а отдает порциями вместе с пакетом второго порта.
-
> sniknik © (17.09.07 09:04) [5]
Попробовать не могу, у меня 9-ка ..
-
если нажимать кнопки по очереди, все работает как ожидается, если нет - как описано в сабже.
-
> Попробовать не могу, у меня 9-ка .. в 9-ке работало. но не работал протокол https (в другой прграмме), может не совсем не работал, но какие то проблемы были, не помню, что и заставило поставить 10й (https сразу заработал), а это вот "выплыло" при каком то мелком исправлении (в caption лейбела орфографическую ошибку сделал ;)) и перекомпиляции (хорошо не отослал замену, сначала проверил и заметил... пришлось в старом ресхакером строку поменять и отослать, но это временно, а вдруг еще что понадобиться поменять, где ресхакером не обойдешся?).
umbra © (17.09.07 10:50) [7] т.е. у тебя также? ок. "по очереди" пакеты не приходят увы. ;(
ладно, будет время придётся в исходниках "поковыряться".
-
sniknik © (17.09.07 11:20) [8] не проще 2 сервер сокета иметь?
-
> не проще 2 сервер сокета иметь? >
похоже, именно в этом и дело. Из исходников следует, что для UDP сервера создается 1 слушающий поток, который в цикле проверяет все привязанные адреса и порты, вызывая на каждом RecvFrom. Если есть две привязки и на первую приходит пакет (нажата Button1), то сведения о нем попадают в мемо, а затем слушающий сокет блокируется на второй привязке и остается блокированным до нажатия второй кнопки. Если сначала нажать Button2, то в мемо ничего не появится до нажатия Button1, т.к. слушающий сокет блокирован на первой привязке. В 9-й инди, скорее всего, на каждую привязку создавался отдельный поток. Почему в 10-й сделали иначе - неясно.
-
> не проще 2 сервер сокета иметь? Ну их вообще то не 2 бывает, а ~ от 1 до 30. Не знаю, реализовать массив то просто, но вот будет ли это правильно... (теперь после [10], если там действительно 1 поток на все, слоняюсь к мысли что так правильней т.к., имхо, должен быть поток на каждую прослушку)
-
> должен быть поток на каждую прослушку
Необязательно. Можно организовать и в одном потоке с использованием таймаута в select'е. Но вот почему индейцы это у себя не организовали (коль скоро там один-единственный слушающий поток) - то для меня загадка.
-
> Можно организовать и в одном потоке с использованием таймаута > в select'е.
Я присмотрелся, и таки да, есть там Select(0, AReadSet, nil, nil, ATimeout) . Тогда вопрос - почему это не срабатывает? И почему срабатывало в 9-й?
-
> почему это не срабатывает?
Надо смотреть код листенер-треда ..
Похоже что параметр ATimeout некорректен, точнее его значение при вызове берется не то что нужно или не оттуда откуда нужно ..
-
> Сергей М. © (16.09.07 11:12) [1] > Не знаю как в 10-ке, но и в 9-ке этот код нормально работать > не должен, ибо обрабортчик OnUDPRead в 9-ке вызывается в > дополнительном потоке.
С чего вы взяли, что в дополнительном?
-
> DVM © (17.09.07 16:51) [15]
На сию мысль навело название класса TIdListenerThread.
-
нашел засаду! в модуле IdUDPServer.pas есть метод TIdUDPListenerThread.Run . Жирным выделены мои исправления, подчеркнутые комментарии - то, что было раньше. Протестировал на сабжевом коде - работает нормально, как и ожидается. procedure TIdUDPListenerThread.Run;
var
PeerIP: string;
i, PeerPort, ByteCount: Integer;
ReadableFDSet: TIdSocketList;
begin
ReadableFDSet := nil;
try
if FReadList.SelectReadList(ReadableFDSet, AcceptWait) then
for i := 0 to ReadableFDSet.Count - 1 do try
if not Stopped then begin
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
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;
-
правда смотрел последний снэпшот инди - там в TIdUDPServer для каждой привязк создается отдельный поток. Чем-то их select не устроил.
-
> ReadableFDSet := nil;
А эт зачем ? Для пущей уверенности что ли ?)
|