Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2006.11.12;
Скачать: [xml.tar.bz2];

Вниз

Проблемы при работе с Indy   Найти похожие ветки 

 
Cyrax ©   (2006-08-26 18:46) [0]

Столкнулся я с такой проблемой. При использовании компонента TIdTCPClient при отсоединении от сервера не освобождается порт, занимаемый клиентом. Отсоединяюсь я методом Disconnect (пробовал и DisconnectSocket). При каждом очередном соединении клиента ОС выделяет ему новый порт. Если же задать порт клиента самому, то при повторном соединении возникает ошибка - порт то остался занятым...
С TIdTCPServer такой проблемы не возникает.

Что самое интересное, пример работы с TIdTCPServer и TIdTCPClient (IdTCPDemo), скачанный с инета, показывает те же результаты.
На форумах читал, как обсуждали глюки некоторых компонентов Indy.
Так вот, может это очередной глюк? Или отсоединяться (клиенту) нужно как то по-другому?

Правильно ли я понимаю механизм соединения клиента с сервером:
При попытке клиента соединиться с сервером (метод TIdPeerThread.Run на клиенте) на стороне сервера вначале создаётся поток для обслуживания этого клиента. Далее происходит попытка установления связи с клиентом. Событие OnExecute на сервере генерится именно после создания потока. При этом возможна ситуация, когда поток создан, а соединение не установлено. При последующем успешном соединении этого же клиента используется тот же поток.
Если я в чём-то ошибаюсь, прошу меня поправить.
Остаётся непонятно, почему в начале обработчика события OnExecute нужно ставить проверку "if not AThread.Terminated". Что, OnExecute генерится ещё и при завершении работы потока?

Есть у меня и другая проблема. При создании объекта TIdTCPServer присвоил полям OnConnect и OnDisconnect имена обработчиков. Напомню, что я пишу модуль, и никакого интерфейса, а тем более визуальных компонентов, нет. Т.е. все объекты я создаю и инициализирую сам... Так вот, в процессе соединения и отсоединения клиентов сервер генерирует события OnConnect и OnDisconnect соответственно и вызывает мои обработчики этих событий (по крайней мере, должен). С коннектом проблем нет. НО: событие OnDisconnect на сервере не генерируется при отсоединении клиента. Это событие генерируется только при завершении работы сервера (IdTCPServer.Active := false). Как я понял из help"а, оно должно генерироваться при каждом отсоединении клиента. Именно так и происходит в случае с сервером и клиентом, созданных визуально путём установки на форму компонентов (в этом случае все необходимые поля инициализируются нужным образом).

Может я не все нужные поля у IdTCPServer проинициализировал?
Вот моя инициализация клиента:

MaxLineLength := DataMax;        
MaxLineAction := maException;
ReadTimeout := 0;
BoundPort := 0;      // чтобы не возникало проблем с занятым портом
Host := "127.0.0.1"; //  пока для проверки
Port := 6002;          


Инициализация сервера:

DefaultPort     := port;
ThreadMgr      := TIdThreadMgrDefault.Create(id_component);
MaxConnections := ClientsMax;
ListenQueue    := 15;
OnExecute      := onexecute;
OnConnect      := onconnect; // мой обработчик
OnDisconnect  := ondisconnect;  // мой обработчик

_______________________
Дополнительные вопросы:
1. Как в винде посмотреть, какие порты в данный момент заняты и какими прогами (средствами винды, без программирования) + как принудительно освободить занятый порт (естественно, с завершением работы проги).
2. Свойство BoundIP клиента указывает IP компа, на котором можно его запускать ?

_______________________
Напоследок хочу узнать мнение тех, кто работал с Indy. Я пишу модуль, который будет обеспечивать связь клиентов с сервером по TCP/IP. Интерфейс представлен 4-мя функциями: инициализация, отправка, получение и завершение. Так вот, целесообразно ли использовать Indy для этих целей. Или проще напрямую работать с winsock, ведь Indy тоже работат через winsock.


 
Dmitrij_K   (2006-08-26 21:02) [1]

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


>  Как в винде посмотреть, какие порты в данный момент заняты
> и какими прогами (средствами винды, без программирования)

Файрволом например Outpost FireWall

> как принудительно освободить занятый порт (естественно,
> с завершением работы проги).

Завершить работу проги

>  Или проще напрямую работать с winsock,

Зависит от твоих знаний


 
Dmitrij_K   (2006-08-26 21:03) [2]

PS такие вопросы задаются в форуме сети


 
Cyrax ©   (2006-08-27 22:26) [3]

Что порт при очередном соединении не обязательно должен быть тем же самым, я согласен. Но не думаю, что винда будет выделять произвольный порт (из свободных). Наиболее вероятный алгоритм - выделение первого свободного (из определённого диапазона). Если это так, то при очередном соединении клиента ему должен быть выделен тот же порт, что и при предыдущем (если за время между соединениями никакие другие проги не занимали порты).
В подтверждение повторю вопрос, заданный в первом сообщении:
Почему при повторном соединении клиента генерится ошибка, связанная с binding"ом сокета? (это в том случае, когда мы прямо укзываем в клиенте порт привязки)


Файрволом например Outpost FireWall

А как насчёт стандартных средств винды (например, брандмауэр Windows).


> Завершить работу проги

Остроумно. Но я имел ввиду ситуацию, когда прога, которой был выделен порт, при завершении его не освобождает по каким-либо причинам (например, прога глючная). Или таких ситуаций быть не пожет (при завершении проги порт в любом случае будет освобождён виндой) ?


такие вопросы задаются в форуме сети

Вопросы об освобождении портов и механизме соединения клиента с сервером - да. Но о генерации событий OnExecute и OnDisconnect всё-таки в компонентах...


 
Cyrax ©   (2006-08-27 22:30) [4]

По поводу механизма соединения клиента и сервера.
Прошу меня поправить, если невломы писать...


 
Ketmar ©   (2006-08-27 22:34) [5]

> [3] Cyrax ©   (27.08.06 22:26)
> при очередном соединении клиента ему должен быть выделен
> тот же порт, что и при предыдущем

кто это кому и что должен? можно пошерстить winsock, если не устраивает логика работы. или своё написать. лично я исходников winsock не видел, и предоположения не делаю. в силу чайниковости, наверное.


 
Dmitrij_K   (2006-08-27 23:27) [6]


> А как насчёт стандартных средств винды (например, брандмауэр
> Windows).

В win нет стандартных средств, Outpost FireWall имхо самый инф
ормативный из тех что я видел

> Или таких ситуаций быть не пожет (при завершении проги порт в любом случае будет освобождён виндой?

имхо так


 
Cyrax ©   (2006-08-28 00:09) [7]

кто это кому и что должен?
Уточню формулировку: "При очередном соединении клиента ему выделяется порт с номером... Короче, ясно ведь, что имеется ввиду!  Замечу: при условии, что другие проги за это время не занимали и не освобождали портов...
...если не устраивает логика работы...
О какой логике работы идёт речь?
Пока прозвучала только моя версия, которая подтверждается практически. Она меня вполне устраивает... Вопрос в другом - освобождается ли порт?

Насчёт предположений.
Обращаюсь всё-таки к тем, кто знает. Пусть порпавят, если ошибаюсь...
А лезть для этого в исходники - изврат...

Кто нибудь скажет, почему ошибка то возникает !!! (моё предыд. сообщение). Тогда и "споры ни о чём" разрешатся...


 
Cyrax ©   (2006-08-28 00:11) [8]

Dmitrij_K, может всё-таки скажешь, в чём я ошибаюсь по поводу механизма...


 
Slym ©   (2006-08-28 06:53) [9]

Клиент вообще не должен думать о своем порте, ему хватает сведений о порте сервера. Порт клиента система сама выделит свободный порт из разрешенного диапазона (>1024)
И зачем прибиндивать клиентский сокет?
при завершении проги, все Хендлы обнуляются, а нулевые освобождаются...


 
Сергей М. ©   (2006-08-28 08:35) [10]


> Cyrax ©   (26.08.06 18:46)


Событие TIdTCPServer.OnExecute возбуждается циклически (а не однократно !) в контексте соответствующего потока TIdPeerThread после успешного установления соединения с клиентом.
Проверка флага Terminated нужна для оперативного реагирования на команду деактивации сервера (IdTCPServer.Active := False).


 
Cyrax ©   (2006-08-28 13:03) [11]

Наконец-то появились нормальные ответы...
[i]
> Slym ©   (28.08.06 06:53)
[/i]
Прибиндивать сокет клиента  - не моя идея, это требование организации (возможность указания порта клиента).
Звучит, конечно, навязчиво... Но всё-таки: когда указываем порт клиента, то при повторном соединении возникает ошибка привязки сокета (без завершения работы проги - соединились, отсоединились, соединились => error). Напрашивается на то, что при отсоединении порт не освобождается... Возможно, отсоединяюсь не так как нужно (см. Cyrax ©   (26.08.06 18:46)). Здесь уже надо знать Indy...
[i]
> Сергей М. ©   (28.08.06 08:35)
[/i]
Циклически после успешного соединения - я так понимаю, генерируется однократно после каждого успешного соединения (в рамках потока).
+ генерируется при завершении потока (после деактивации сервера)?

Как, всё-таки быть с генерацией события OnDisconnect на сервере ? (см. Cyrax ©   (26.08.06 18:46)). Когда это событие должно генерироваться - при отсоединении каждого из клиентов или при завершении работы сервера (при деактивации сервера) ?


 
Сергей М. ©   (2006-08-28 13:19) [12]


> Циклически после успешного соединения - я так понимаю, генерируется
> однократно после каждого успешного соединения (в рамках
> потока).


Нет, не правильно.
После успешного установления соединения создается новый экз-р TIdPeerThread (или берется уже готовый, ранее созданный и ждущий своего использования в пуле тредпул-менеджера), в контексте которого с этого момента циклически вызывается обработчик OnExecute вплоть до самой "смерти" соединения.


 
Cyrax ©   (2006-08-28 15:15) [13]

Что значит "циклически" ? В какие моменты времени ?
И как насчёт дективации сервера ?


 
Сергей М. ©   (2006-08-28 15:40) [14]


> Что значит "циклически" ?


Циклически означает "в цикле".

ты в состоянии заглянуть в исх-ки сервера и проанализировать их ?


> как насчёт дективации сервера ?


А что насчет нее ?
Серверный диспетчер при поступлении "команды" Active := False командует всем работающим своим потокам "Terminate" и ждет пока все созданные им потоки завершатся.


 
Cyrax ©   (2006-08-28 16:16) [15]


Сергей М. ©   (28.08.06 15:40)

Хорошо, гляну в исходники...

Но следующие вопросы остаются открытыми:
1. При отсоединении клиента (disconnect, либо disconnectsocket) порт не освобождается (см. Cyrax ©   (28.08.06 13:03)).
2. Серверные OnDisconnect"ы не вызываются при отсоединении клиентов (только при завершении работы проги / деактивации сервера).


 
Сергей М. ©   (2006-08-28 16:28) [16]


> Cyrax ©   (28.08.06 16:16) [15]
>
>


Не надо месить в одну кучу сервера и клиента.
Либо ты о сервере, либо о клиенте.
Ты о ком ?


 
Cyrax ©   (2006-08-28 16:53) [17]

В первом вопросе - ... лучше переформулирую:
1. При отсоединении клиента (disconnect, либо disconnectsocket на клиенте) порт, занимаемый клиентом не освобождается (см. Cyrax ©   (28.08.06 13:03)).
2. OnDisconnect"ы (на сервере) не вызываются при отсоединении клиентов (только при завершении работы сервера / деактивации сервера).


 
Cyrax ©   (2006-08-28 17:10) [18]

Насчёт события OnExecute, - вроде бы разобрался...
Если кто не знает:
После установления соединения генерируется OnExecute, выполняется и далее генерируется циклически следующим образом:
Проверяется, подсоединён ли клиент, не был ли вызван Disconnect в OnExecute, отсутствуют ли фатальные ошибки, не было ли возбуждено необработанное исключение в OnExecute и активен ли ещё сервер.
Если всё нормально, то событие генерируется снова, и т.д.
Среди перечисленных проверок нет проверки потока на Terminated, поэтому эта проверка необходима вначале обработчика OnExecute...


 
dwar ©   (2006-08-29 07:36) [19]

А какой INDY используется ?


 
Cyrax ©   (2006-08-29 10:18) [20]

9-й.


 
Сергей М. ©   (2006-08-29 10:47) [21]


> 1. При отсоединении клиента (disconnect, либо disconnectsocket
> на клиенте) порт, занимаемый клиентом не освобождается (см.
>  Cyrax ©   (28.08.06


Не знаю, у меня все работает.

при, к примеру, BoundPort = 555555 следующий код

procedure TForm2.Button1Click(Sender: TObject);
begin
 if idtcpclient1.Connected then
   idtcpclient1.Disconnect
 else
   idtcpclient1.Connect();
end;

не вызывает никаких исключений, сколько бы раз ни давить кнопку.


> 2. OnDisconnect"ы (на сервере) не вызываются при отсоединении
> клиентов (только при завершении работы сервера / деактивации
> сервера).


Это нормально, так и должно быть.


 
Cyrax ©   (2006-08-29 21:40) [22]

По поводу освобождения порта - ещё раз всё проверю... Если что, скину исходники для теста...

Теперь насчёт Disconnect"ов. Согласен, что на сервере OnDisconnect не должен вызываться после отсоединения клиента. OnDisconnect генерируется при вызове метода Disconnect сервера.
Сомнения у меня были вызваны примером работы с Indy (http://www.delphi.pnz.ru/components2/Indy9Demos.exe, IdTCPDemo), где факт отсоединения клиента сразу же фиксировался на сервере (на сервере вызывается OnDisconnect). Каким образом, несовсем понятно. Обработчик OnDisconnect (ServerDisconnect) как раз и обрабатывает отсоединение клиента. Но для вызова этого обработчика (через событие OnDisconnect) нужно выполнить метод Disconnect на сервере после очередной проверки AThread.Connection.Connected. Такая проверка стоит только в обработчике OnExecute, причём в случае отсоединения клиента ничего там не делается (в частности, не вызывается метод Disconnect)...

Фрагмент кода для сервера:

procedure TServerFrmMain.ServerExecute(AThread: TIdPeerThread);
var
 ActClient, RecClient: PClient;
 CommBlock, NewCommBlock: TCommBlock;
 RecThread: TIdPeerThread;
 i: Integer;
 F: integer;
begin
 if not AThread.Terminated and AThread.Connection.Connected then
 begin
   AThread.Connection.ReadBuffer (CommBlock, SizeOf (CommBlock));
   ActClient := PClient(AThread.Data);
   ActClient.LastAction := Now;  // update the time of last action

   if (CommBlock.Command = "MESSAGE") or (CommBlock.Command = "DIALOG")
     or (CommBlock.Command = "FILE") then
   begin  // "MESSAGE": A message was send - forward or broadcast it
       ...

       // Запись в файл
       F := FileCreate("2.txt");
       FileWrite(F, CommBlock.Msg, 100);
       FileClose(F);

       with Clients.LockList do
       try
         for i := 0 to Count-1 do  // iterate through client-list
               begin
           RecClient := Items[i];           // get client-object
           RecThread := RecClient.Thread;     // get client-thread out of it
           RecThread.Connection.WriteBuffer(NewCommBlock, SizeOf(NewCommBlock), True);  // send the stuff
         end;
       finally
         Clients.UnlockList;
       end;
     end
     else
     begin  // receiver given - search him and send it to him
       NewCommBlock := CommBlock; // again: nothing to change ;-))
       if CommBlock.Command = "FILE" then
         Protocol.Lines.Add(TimeToStr(Time)+" Sending "+CommBlock.Command+" to ""+CommBlock.ReceiverName
           +": received file " + CommBlock.FileName)
       else
         Protocol.Lines.Add(TimeToStr(Time)+" Sending "+CommBlock.Command+" to ""+CommBlock.ReceiverName+"": ""+CommBlock.Msg+""");

       with Clients.LockList do
       try
         for i := 0 to Count-1 do
         begin
           RecClient:=Items[i];
           if RecClient.DNS=CommBlock.ReceiverName then  // we don"t have a login function so we have to use the DNS (Hostname)
           begin
             RecThread:=RecClient.Thread;
             RecThread.Connection.WriteBuffer(NewCommBlock, SizeOf(NewCommBlock), True);
           end;
         end;
       finally
         Clients.UnlockList;
       end;
     end;
   end
   else
   begin  // unknown command given
     Protocol.Lines.Add (TimeToStr(Time)+" Unknown command from ""+CommBlock.MyUserName+"": "+CommBlock.Command);
     NewCommBlock.Command := "DIALOG";       // the message should popup on the client"s screen
     NewCommBlock.MyUserName := "[Server]";  // the server"s username
     NewCommBlock.Msg := "I don""t understand your command: ""+CommBlock.Command+""";  // the message to show
     NewCommBlock.ReceiverName := "[return-to-sender]"; // unnecessary

     AThread.Connection.WriteBuffer (NewCommBlock, SizeOf (NewCommBlock), true);  // and there it goes...
   end;
 end;
end;

procedure TServerFrmMain.ServerDisconnect(AThread: TIdPeerThread);
var
 ActClient: PClient;

begin
 ActClient := PClient(AThread.Data);
 Protocol.Lines.Add (TimeToStr(Time)+" Disconnect from ""+ActClient^.DNS+""");
 try
   Clients.LockList.Remove(ActClient);
 finally
   Clients.UnlockList;
 end;
 FreeMem(ActClient);
 AThread.Data := nil;
end;

Фрагмент кода для клиента:

 TClientHandleThread = class(TThread)
            private
                   CB: TCommBlock;
                   procedure HandleInput;
            protected
                   procedure Execute; override;
                   end;
var
 ClientFrmMain: TClientFrmMain;
 ClientHandleThread: TClientHandleThread;   // variable (type see above)

implementation
{$R *.DFM}
procedure TClientHandleThread.HandleInput;
begin
 if CB.Command = "MESSAGE" then
 ...
end;

procedure TClientHandleThread.Execute;
begin
 while not Terminated do
 begin
   if not ClientFrmMain.Client.Connected then
     Terminate
   else
   try
     ClientFrmMain.Client.ReadBuffer(CB, SizeOf (CB));
     Synchronize(HandleInput);
   except
   end;
 end;
end;

procedure TClientFrmMain.CBClientActiveClick(Sender: TObject);
begin
 if CBClientActive.Checked then
 begin
   try
     Client.Connect(10000);  // in Indy < 8.1 leave the parameter away

     ClientHandleThread := TClientHandleThread.Create(True);
     ClientHandleThread.FreeOnTerminate:=True;
     ClientHandleThread.Resume;
   except
     on E: Exception do MessageDlg ("Error while connecting:"+#13+E.Message, mtError, [mbOk], 0);
   end;
 end
 else
 begin
   ClientHandleThread.Terminate;
   Client.Disconnect;
 end;

 ButtonSend.Enabled := Client.Connected;
 CBClientActive.Checked := Client.Connected;
end;

procedure TClientFrmMain.ButtonSendClick(Sender: TObject);
var
 ...
begin
 ...
 if CommBlock.Command = "FILE" then
 ...
 Client.WriteBuffer (CommBlock, SizeOf (CommBlock), true);
end;
 ...
end.

Файл .dfm сервера:

 ...
 object Server: TIdTCPServer
   Bindings = <>
   CommandHandlers = <>
   DefaultPort = 47110
   Greeting.NumericCode = 0
   MaxConnectionReply.NumericCode = 0
   OnConnect = ServerConnect
   OnExecute = ServerExecute
   OnDisconnect = ServerDisconnect
   ReplyExceptionCode = 0
   ReplyTexts = <>
   ReplyUnknownCommand.NumericCode = 0
   ThreadMgr = IdThreadMgrDefault1
   Left = 184
   Top = 76
 end
 object IdThreadMgrDefault1: TIdThreadMgrDefault
   Left = 224
   Top = 76
 end
 ...

________________
И другой вопрос...
В моём модуле (см. Cyrax c   (26.08.06 18:46)) обработчик события OnExecute пуст, поскольку все операции по чтению и отправке данных осуществляются через интерфейсные функции. Не будут ли генерироваться исключения, если я не буду обрабатывать OnExecute?
И вообще, неплохо было бы услышать советы о том, что можно реализовать в обработчике OnExecute для модуля...


 
Сергей М. ©   (2006-08-30 08:34) [23]


> Cyrax ©   (29.08.06 21:40) [22]



> факт отсоединения клиента сразу же фиксировался на сервере
> (на сервере вызывается OnDisconnect). Каким образом, несовсем
> понятно


Все довольно просто.

Иллюстрация в псевдокоде:

//тело обработчика события сервера OnExecute

try

SomeRequest := SomeSocketReadMethod(..., SomeReadTimeout);
...
SomeSocketWriteMethod(SomeResponse);
...
except
Один из вызванных методов приема/передачи возбудил исключение.
Если это исключение НЕ вызвано превышением таймаута ожидания чтения, то
  Disconnect; // вод здесь и вызывается обработчик OnDisconnect
end;


> поскольку все операции по чтению и отправке данных осуществляются
> через интерфейсные функции


Это как ?


 
Cyrax ©   (2006-08-30 10:09) [24]

В Demo-примере используется другая схема. Там в обработчике OnExecute вначале стоит проверка
if not AThread.Terminated and AThread.Connection.Connected
и если всё нормально, то происходит чтение/запись. При этом никаких try"ев для операций чтения/записи там нет (только для LockList"а). Если условие не выполняется, то ничего не делается (в этом обработчике), т.е. работа обработчика завершается...
И вообще, на сервере нигде явно не вызывается метод Disconnect...

По поводу интерфейсных функций. Любая операция приёма и отправки данных происходит только по запросу главной программы, которая будет использовать мой модуль. Т.е. чтение/запись осуществляется только в момент вызова главной программой соответствующих функций модуля.
А в этих функциях я читаю и отправляю через WriteBuffer/ReadBuffer и возвращаю данные в главную прогу...


 
Сергей М. ©   (2006-08-30 10:23) [25]


> Cyrax ©   (30.08.06 10:09) [24]
>
> В Demo-примере используется другая схема. Там в обработчике
> OnExecute вначале стоит проверка


Да, стоит. Но она не гарантирует, что исключительные ситуации (в момент собственно приема/передачи) не возникнут.


> в этих функциях я читаю и отправляю через WriteBuffer/ReadBuffer


Эти ф-ции должны вызывать не не иначе как в контексте соответствующего TIdPeerThread, т.е. в oбработчике OnExecute.


 
Cyrax ©   (2006-08-30 14:45) [26]

Да, в примере этот случай не учтён...
Тем не менее OnDisconnect каким-то образом генерируется при отсоединении клиента. Явных вызовов метода Disconnect на сервере нет...
Или в случае невыполнения условия AThread.Connection.Connected (в этот момент уже становится известно, что клиент отсоединился) на сервере вызывается метод Disconnect ?  Где-то же он должен вызываться (неявно)...

Насчёт функций. Главная программа вызывает функцию чтения данных по сети. Эта функция по очереди проверяет потоки всех клиентов (эти потоки у меня хранятся в специальной структуре с данными о клиентах) и ищет те, в контексте которых есть входные данные. Затем читает все входные данные из этого потока и возвращает через параметры размер прочитанных данных, сами данные и id клиента, от которого данные получены. При следующем вызове этой функции просмотр потоков начинается со следующего (в порядке очереди). Главная программа, сформировав ответ, вызывает функцию отправки сообщения и передаёт ей данные, размер и идентификатор клиента.
Таким образом, обработчик OnExecute у меня пока пуст...


 
Сергей М. ©   (2006-08-31 13:16) [27]


> ли в случае невыполнения условия AThread.Connection.Connected
> (в этот момент уже становится известно, что клиент отсоединился)
> на сервере вызывается метод Disconnect ?


Наверно, так и есть.
Посмотри сам в исходниках сервера.


> вызывает функцию отправки сообщения


Мне не понятно, где у тебя вызываются собственно Write-методы объекта Connection ..


 
Cyrax ©   (2006-08-31 22:19) [28]

Функция записи, которую вызывает главная программа, по переданному ей идентификатору клиента из списка TThreadList ищет соответствующий поток AThread (у каждого потока свойство Data указывает на структуру данных с информацией о клиенте, в частноости, id), затем: Athread.Connection.WriteBuffer.
[Если что, могу скинуть исходники...]
Т.е. все операции чтения/записи происходят отдельно и незавсимо от OnExecute. OnExecute мне, получается, и не нужен вообще...

Кстати, хочу уточнить ситуацию с определением размера входных данных.
Читаю я с помощью ReadBuffer, поэтому мне надо знать число байтов на входе. Нашёл я два метода Connection"а, которые позволяют это сделать:
1. ReadFromStack. Функция возвращает число байтов и читает всё во внутренний буфер. Я внутренний буфер не использую - он мне не нужен. Поэтому эту функцию я использую только для получения размера входных данных:

data_len := AThread.Connection.ReadFromStack(true,0,false);  // читает во внутренний буфер, которого я не создавал... (скорее всего буфер создаётся по умолчанию)
if data_len > 0 then AThread.Connection.ReadBuffer(data,data_len)
else ...

Но эту функцию не рекомендуют использовать.

2. CurrentReadBuffer. Функция читает все данные и возвращает их в виде строки. По длине этой строки затем можно определить размер данных. Но в этом случае хошь-нехошь придётся использовать внутренний буфер, поскольку функция читает из внутреннего буфера (ReadFromStack - прямо из стека).

Таким образом, нет функций, определяющих размер входных данных и не использующих внутренний буфер (или я не нашёл...)


 
Сергей М. ©   (2006-09-01 08:46) [29]


> Cyrax ©   (31.08.06 22:19) [28]


Метод Connection.WriteBuffer должен вызываться в контексте обслуживающего данное соединение потока ! Обработчик OnExecute как раз и выполняется в этом контексте.


> хочу уточнить ситуацию с определением размера входных данных


Знать этот размер вовсе не обязательно.


 
Cyrax ©   (2006-09-01 20:00) [30]


>Метод Connection.WriteBuffer должен вызываться в контексте обслуживающего данное соединение потока

Я его вызываю из первичного потока с использованием объекта AThread типа TIdPeerThread. Во всех вторичных потоках (для клиентов) у меня крутится пустой обработчик OnExecute... Доступ к AThread"ам всех клиентов, естественно, имеется из любого потока процесса. Вот я и пользуюсь этими Thread"ами из первичного потока.

Знать этот размер вовсе не обязательно

Если я не буду знать число байтов на входе, то как я прочитаю все данные, не блокируя работу потока? Ведь надо указать число считываемых байтов...


 
Cyrax ©   (2006-09-02 20:36) [31]

По поводу вызова метода Disconnect на сервере. Ни хр он не вызывается в момент проверки AThread.Connection.Connected в обработчике OnExecute, когда клиент отсоединён. И даже AThread.Terminated не приводит к вызову Disconnect.
Поэтому, хочу уточнить (Cyrax ©   (28.08.06 17:10) [18]): проверка, подсоединён ли клиент, перед очередным вызовом OnExecute может дать положительный результат, даже если клиент в настоящий момент отсоединён (в этом случае проверка AThread.Connection.Connected вначале обработчика OnExecute также даст положительный результат). Дело в том, что об отсоединении клиента становится известно только в момент чтения/записи в соответствующем потоке. В случае, если клиент отсоединён, функции чтения/записи вызовут исключение, стандартный обработчик которого как раз и вызывает метод Disconnect сервера (этот вызов генерирует событие OnDisconnected, где выполняются наши действия). После этого проверка, подсоединён ли клиент, перед очередным вызовом OnExecute и AThread.Connection.Connected будут давать уже отрицательный результат.


 
Cyrax ©   (2006-09-02 20:38) [32]

Сергей М., твой пример (Сергей М. ©   (29.08.06 10:47) [21]) я проверил. Результаты те же...
Ниже привожу код сервера и клиента. Попробуй скомпилить и проверить без изменений.

client.dpr

program client;
uses
 Forms,
 main_unit in "main_unit.pas" {Form1};
{$R *.res}

begin
 Application.Initialize;
 Application.CreateForm(TForm1, Form1);
 Application.Run;
end.

main_unit.dfm (для клиента)

object Form1: TForm1
 Left = 305
 Top = 218
 BorderIcons = [biSystemMenu, biMinimize]
 BorderStyle = bsSingle
 Caption = "TCP Client tester"
 ClientHeight = 162
 ClientWidth = 257
 Color = clBtnFace
 Font.Charset = DEFAULT_CHARSET
 Font.Color = clWindowText
 Font.Height = -11
 Font.Name = "MS Sans Serif"
 Font.Style = []
 OldCreateOrder = False
 PixelsPerInch = 96
 TextHeight = 13
 object Label1: TLabel
   Left = 8
   Top = 144
   Width = 39
   Height = 13
   Caption = "status:"
   Font.Charset = DEFAULT_CHARSET
   Font.Color = clWindowText
   Font.Height = -11
   Font.Name = "MS Sans Serif"
   Font.Style = [fsBold]
   ParentFont = False
 end
 object Label2: TLabel
   Left = 56
   Top = 144
   Width = 77
   Height = 13
   Caption = "disconnected"
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
   Font.Color = clBlue
   Font.Height = -11
   Font.Name = "MS Sans Serif"
   Font.Style = [fsBold]
   ParentColor = False
   ParentFont = False
 end
 object Button1: TButton
   Left = 8
   Top = 112
   Width = 105
   Height = 25
   Caption = "Connect"
   TabOrder = 0
   OnClick = Button1Click
 end
 object Memo1: TMemo
   Left = 8
   Top = 16
   Width = 241
   Height = 85
   ReadOnly = True
   TabOrder = 1
 end
 object IdTCPClient1: TIdTCPClient
   MaxLineAction = maException
   ReadTimeout = 0
   OnDisconnected = IdTCPClient1Disconnected
   BoundPort = 55559
   Host = "127.0.0.1"
   OnConnected = IdTCPClient1Connected
   Port = 6002
   Left = 192
   Top = 8
 end
end

main_unit.pas (для клиента)

unit main_unit;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, IdBaseComponent, IdTCPClient,
 StdCtrls, IdTCPConnection, IdComponent;

type
 TForm1 = class(TForm)
   Button1: TButton;
   IdTCPClient1: TIdTCPClient;
   Memo1: TMemo;
   Label1: TLabel;
   Label2: TLabel;
   procedure Button1Click(Sender: TObject);
   procedure IdTCPClient1Connected(Sender: TObject);
   procedure IdTCPClient1Disconnected(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 if IdTCPClient1.Connected
 then IdTCPClient1.Disconnect
 else IdTCPClient1.Connect;
end;

procedure TForm1.IdTCPClient1Connected(Sender: TObject);
begin
 self.Label2.Caption := "connected";
 self.Button1.Caption := "disconnect";
end;

procedure TForm1.IdTCPClient1Disconnected(Sender: TObject);
begin
 self.Label2.Caption := "disconnected";
 self.Button1.Caption := "connect";
end;

end.

server.dpr

program server;

uses
 Forms,
 main_unit in "main_unit.pas" {Form1};

{$R *.res}

begin
 Application.Initialize;
 Application.CreateForm(TForm1, Form1);
 Application.Run;
end.

main_unit.dfm (для сервера)

object Form1: TForm1
 Left = 294
 Top = 245
 BorderIcons = [biSystemMenu, biMinimize]
 BorderStyle = bsSingle
 Caption = "TCP Server tester"
 ClientHeight = 130
 ClientWidth = 258
 Color = clBtnFace
 Font.Charset = DEFAULT_CHARSET
 Font.Color = clWindowText
 Font.Height = -11
 Font.Name = "MS Sans Serif"
 Font.Style = []
 OldCreateOrder = False
 PixelsPerInch = 96
 TextHeight = 13
 object protocol: TMemo
   Left = 8
   Top = 8
   Width = 241
   Height = 113
   ReadOnly = True
   TabOrder = 0
 end
 object IdTCPServer1: TIdTCPServer
   Active = True
   Bindings = <>
   CommandHandlers = <>
   DefaultPort = 6002
   Greeting.NumericCode = 0
   MaxConnectionReply.NumericCode = 0
   OnConnect = IdTCPServer1Connect
   OnExecute = IdTCPServer1Execute
   OnDisconnect = IdTCPServer1Disconnect
   ReplyExceptionCode = 0
   ReplyTexts = <>
   ReplyUnknownCommand.NumericCode = 0
   Left = 216
   Top = 88
 end
end

main_unit.pas (для сервера)

unit main_unit;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, IdBaseComponent, IdComponent, IdTCPServer, IdThreadMgrDefault,
 StdCtrls, IdThreadMgr;

type
 TForm1 = class(TForm)
   IdTCPServer1: TIdTCPServer;
   protocol: TMemo;
   procedure IdTCPServer1Execute(AThread: TIdPeerThread);
   procedure IdTCPServer1Connect(AThread: TIdPeerThread);
   procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var num: integer;
begin
 if not AThread.Terminated and AThread.Connection.Connected
 then with AThread.Connection do
   begin
      num := AThread.Connection.ReadInteger(true);
   end;                                          
end;

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
 Protocol.Lines.Add(TimeToStr(Time) + " Connection with "" + AThread.Connection.LocalName + "": " + IntToStr(AThread.Connection.Socket.Binding.PeerPort));
end;

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
 Protocol.Lines.Add (TimeToStr(Time)+" Disconnect");
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 IdTCPServer1.Active := true;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 IdTCPServer1.Active := False;
end;

end.


При повторном соединении клиента вырабатывается исключение !!!
Глюк: хотя свойство BoundPort имеет тип Integer (4 байта), в конце концов он урезается до word (2 байта). Почему так происходит, непонятно... Поэтому в примере я указал порт клиента 55555.

__________________________________________________________________
Кто знает, как определить число байт на входе или как считать все байты, не замораживая работу потока ?


 
Dmitrij_K   (2006-09-03 19:56) [33]


>  свойство BoundPort имеет тип Integer (4 байта), в конце
> концов он урезается до word (2 байта)

потому что порт это тип Word 2 байта

> Кто знает, как определить число байт на входе или как считать
> все байты, не замораживая работу потока ?

 AThread.Connection.ReadFromStack; - прочитать все данные из сокета (какие есть на данный момент) во внутренний буфер InputBuffer
AThread.Connection.InputBuffer - внутренний буфер, наследник от TMemoryStream со всеми вытекающими

---
 AThread.Connection.ReadFromStack;
 байт на входе := AThread.Connection.InputBuffer.Size;

---


 
Cyrax ©   (2006-09-03 20:24) [34]

> Dmitrij_K   (03.09.06 19:56) [33]
> потому что порт это тип Word 2 байта

Да, но тип свойства BoundPort - Integer (4 байта)...

> AThread.Connection.ReadFromStack;
> байт на входе := AThread.Connection.InputBuffer.Size;

А без внутреннего буфера ?


 
Dmitrij_K   (2006-09-03 21:19) [35]


> Да, но тип свойства BoundPort - Integer (4 байта)...

захотелось им так, в TCP/IP порт это 2 байта т.е. тип Word


> А без внутреннего буфера ?

ioctlsocket(IdTCPClient1.Socket.Binding.Handle, FIONREAD, j);
в j кол-во принятых байт но еще не прочитанных из сокета


 
Сергей М. ©   (2006-09-04 08:45) [36]


> Cyrax ©   (01.09.06 20:00) [30]
> Я его вызываю из первичного потока


Ну и накой ляд тогда, спрашивается, существует TIdPeerThread ?
Это же поток транспортного уровня).. Он для того и существует, чтобы обслуживать транспортные гнездовые операции, не блокируя при этом прочие потоки процесса.


 
Cyrax ©   (2006-09-04 10:05) [37]

Чё то ничего не понял... трудно соображать поутру... наверное, от Ketmar"а пошло...

Если я буду передавать данные в OnExecute, то ... да мне нужно это делать именно по запросу главной программы...


 
Сергей М. ©   (2006-09-04 10:07) [38]


> мне нужно это делать именно по запросу главной программы


Ну и делай) ... в чем проблема-то ? не знаешь как синхронизировать потоки или что ?


 
Сергей М. ©   (2006-09-04 10:14) [39]

with AThread do
while not Terminated and Connection.Connected and GetDataToSend(SomeDataToSend, SomeTimeout) do begin
 try
   Connection.SomeWriteMethod( ..SomeDataToSend ... );
 except
   Connection.Disconnect;
 end;
end;


 
Cyrax ©   (2006-09-04 10:14) [40]

Ну и делай)

Ну а варианты то какие ?  Например, с OnExecute.

в чем проблема-то

Да мне нужно обнаружить отсоединение клиента как можно раньше, хотя бы в момент чтения/записи. Но если это делать из первичного потока, то исключений, вызывающих OnDisconnecting, не вырабатывается...



Страницы: 1 2 3 4 вся ветка

Форум: "Начинающим";
Текущий архив: 2006.11.12;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.65 MB
Время: 0.038 c
15-1147981014
Eraser
2006-05-18 23:36
2006.11.12
Remote Office Manager - бета тестирование


2-1161785055
lorn
2006-10-25 18:04
2006.11.12
Delphi->EMAIL


1-1159719430
Master_
2006-10-01 20:17
2006.11.12
EXCEL документ в DLL ?


2-1161633221
5Stas5
2006-10-23 23:53
2006.11.12
Как сделать многострочный LISTBOX используя только WIN API


2-1161930250
ГореПрограммер
2006-10-27 10:24
2006.11.12
Размер файла





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский