Форум: "Начинающим";
Текущий архив: 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.64 MB
Время: 0.05 c