![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблемы при работе с Indy [D7, WinXP]
Cyrax © (2006-08-26 18:46:00)Столкнулся я с такой проблемой. При использовании компонента 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.
Сергей М. (2006-10-12 08:28:00) [41]
> Приходится рыться в инете.
> (ошибаюсь ?)
>
В дан.случае ошибаешься.
Эта функциональность специфична именно для Delphi IDEб и где еще как не в "родной" справке к IDE искать ее описание ?
> Не думаю, что из Search -> Find Error что-то толковое выйдет
Если ошибка именно в твоем коде, Search -> Find Error покажет тебе строчку, при выполнении которой произошла эта ошибка.
Cyrax (2006-10-11 23:00:00) [42]Справку читай, там все описано.
Уж не особо я полагаюсь на справку в Delphi...
Не особо ей доверяю...
Она не достаточно практична... Приходится рыться в инете.
(ошибаюсь ?)
Да и вообще, ассемблерным дебаггером в своё время активно пользовался.
В данном же случае меня интересует вопрос, касающийся ошибки...
Не думаю, что из Search -> Find Error что-то толковое выйдет. По-моему, достаточно вызова функции, которую я привёл выше, и парочку уточнений по моему модулю - и проблема будет решена (естественно, с помощью тех, кто работал с SetSockOpt)...
Сергей М. (2006-10-11 17:43:00) [43]
> Cyrax © (11.10.06 17:31) [113]
> этот же сокет передатчика является и сокетом приёмника
"Приемнику" алгоритм Нанеля по барабану - этот алгоритм актуален лишь для передающей стороны.
> что я должен высмотреть в этих регистрах и дампах
Справку читай, там всеописано.
Cyrax (2006-10-11 17:31:00) [44]Надеюсь, вся эта катавасия у тебя происходит для гнезда-передатчика.
Да. Но этот же сокет передатчика является и сокетом приёмника - двусторонняя связь...
Вот по именно такому-то адресу и ищи ошибку.
см. Search -> Find Error ..
И что я должен высмотреть в этих регистрах и дампах...
Сергей М. (2006-10-10 16:41:00) [45]
> по такому-то адресу
Вот по именно такому-то адресу и ищи ошибку.
см. Search -> Find Error ..
Надеюсь, вся эта катавасия у тебя происходит для гнезда-передатчика.
Cyrax (2006-10-10 16:17:00) [46]Не пашет:
const Id_SO_True: boolean = true;
Id_SO_False: boolean = false;
begin
Self.Socket.Binding.SetSockOpt(Id_SOL_SOCKET, Id_TCP_NODELAY, Char(@Id_SO_False), SizeOf(Id_SO_False));
Ошибка типа доступ по такому-то адресу...
Превым параметром ставил и Id_IPPROTO_TCP, и Id_IPPROTO_IP - то же самое...
Сергей М. (2006-10-10 08:49:00) [47]
> До каких пор данные будут буферизоваться (до какого размера)
Это определяется алгоритмом Нагеля.
см. SetSockOpt(TCP_NODELAY)
Cyrax (2006-10-10 08:07:00) [48]И насчёт аккумуляции. До каких пор данные будут буферизоваться (до какого размера). И через какое время они начинают передаваться (если данных для буферизации больше не поступает)...
Cyrax (2006-10-10 08:04:00) [49]Ну, скажем, ковыряться в имплементе функции, обрабатывающей данные, не в моей компетенции. Я лишь вызываю её, когда приходят данные.
Функция работате правильно только тогда, когда вызывается только для одногоочередного прикладного пакета...
Ketmar (2006-10-10 00:16:00) [50]ну и "перепосылай". или сделай разборщик на простом автомате. а буфера -- не трогай. их не зря сделали. %-)
Cyrax (2006-10-09 23:44:00) [51]Ketmar © (09.10.06 23:32) [105]
а зачем? ну, положим, можно. но -- зачем? лучше код напиши правильно.
На основе процессоптеров чтоль...
У меня сервер, который принимает данные от клиента, всего лишь пересылает их аппаратуре, не влезая в прикладной протокол, чтобы разделять прикладные пакеты друг от друга...
Ketmar (2006-10-09 23:32:00) [52]а зачем? ну, положим, можно. но -- зачем? лучше код напиши правильно.
Cyrax (2006-10-09 23:21:00) [53]И как ету аккумуляцию придушить ?
Ketmar (2006-10-09 23:18:00) [54]естественно, аккумулируется в буфере винсока.
Ethereal.
Cyrax (2006-10-09 23:14:00) [55]Что-то я забросил ветку...
Всё-таки половина вопросов была по сетям.
1. Сейчас у меня такая ситуация. Клиент отсылает запросы серверу. 1 запрос - 1 прикладной пакет (10-30 байтов). Сервер поддерживает как SPX/IPX-протокол, так и TCP/IP. На стороне сервера, как только приходят данные (хотя бы 1 байт), вызывается функция, их обрабатывающая. Проверка появления входных данных на серверном сокете происходит в отдельном потоке в цикле OnExecute, т.е. практически непрерывно. В случае с TCP/IP наблюдается ситуация, когда эта функция получает блок данных, соответствующий нескольким запросам клиента (*). Запросы же клиент отсылает отдельно, друг за другом, с некоторыми промежутками. В случае с SPX/IPX функция обрабатывает входные данные отдельными прикладными пакетами, т.е. ситуации, когда функция вызывается после прихода на серверный сокет нескольких клиентских прикладных пакетов, не наблюдается... (для SPX/IPX вызывается другая функция, но выполняющая практически те же действия)
Конечно, такие ситуации в общем случае вполне могут быть. Но настораживает то, что проверка в OnExecute происходит практически непрерывно и появление на серверном сокете очередного прикладного пакета должно быть сразу же зафиксировано и вызвана соответствующая функция, не дожидаясь, пока придёт ещё один прикладной пакет...
з.ы. не объединяются ли прикладные пакеты в один IP-пакет (по "вине" ОС) на клиентской стороне (стороне отправителя), поскольку прикладной пакет намного меньше среднего размера IP-пакета ?
И какие задержки могут привести к таким результатам (*)
Рассматриваемые ситуации с TCP и IPX полностью идентичны за исключением протокола...
2. Какие проги можете посоветовать для слежки за передачей IP-пакетов в винде. Желательно небольшие, чтоб можно было закачать по dialup'у...
Сергей М. (2006-09-25 08:13:00) [56]Дискуссия перешла в русло абстрактной "болтологии" на тему Паскаля, к сетям не имеющей отношения.
Формулируй свои вопросы отдельно в порядке их возникновения, с иллюстрациями в коде, и задавай их в конф-ции "Общие" или "Начинающим".
Cyrax (2006-09-24 23:04:00) [57]Сергей М. © (22.09.06 08:17) [99]
Ну и нафиг тогда тебе Инди со всеми его наворотами ?
Выходит, что нафиг...
В любом случае использовать Indy - не моя идея...
С другой стороны, отсутствие ориентации модуля на какой-либо протокол позволяет использовать его и на серверах, реализующую параллельную обработку клиентов. Для случая однопоточной обработки запросов использование этого модуля для подобных целей было бы невозможно...
11. Задача такова. Есть функция, которая принимает параметр типа PData.
Тип PData - какой-либо типизированный указатель, включая PChar.
При вызове функции в качестве фактического параметра передаётся PData(параметр_типа_Pointer). Но в случае с PChar генерируется статическая ошибка.
Как этого избежать ? Почему PChar не приводится к Pointer ? Ведь это просто указатель на Char...
Дело в том, что среди возможных типов PData должен быть такой, чтобы доступ к элементам осуществлялся путём индексации (как в случае с PChar).
Обязан.
Потому что Destroy - виртуальный метод.
Тогда в своих деструкторах и конструкторах придётся самому вызывать деструкторы и конструкторы базового класса. Не очень то удобно...
Зачем серверу оператор ? Зачем оператору инф-ция об отверженном соединении ?
Допустим, сервер ведёт статистику, собирает и обрабатывает подобную (и не только) информацию. В любой момент можно быдет посмотреть, какие клиенты подключены, каким соединениям и когда было отказано и т.п.
Эта информация в любом случае не помешает...
>> Почему, когда ты в производном классе определяешь конструктор
>> и деструктор, при их вызове (а вернее, до и после, соответственно)
>> автоматически не вызываются конструкторы и деструкторы базового
>> класса ?
>Потому что у тебя ошибка в декларации/реализации/использовании
>конструктора и деструктора "производного" класса (*)
Я так не думаю...
>Ну тогда продолжай в том же духе.
А по моему, виртуальный метод не должен вызывать соответствующий метод базового класса...
Ты считаешь более простым объяснять тебе на пальцах неизвестно что и неизвестно почему происходящее в неизвестно каком коде ?
Дело в том, что для моделирования подобных ситуаций и представлении их в ярко выраженной форме потребуется потратить время на выдумывание такого кода. А в моей программе такие ситуации имеют место, но не в такой выраженной форме, чтобы только по коду можно было понять вопрос, да и части кода будут из разных мест программы + всё-равно потребуются письменные комментарии и разьяснения...
Те которые ждут акцептирования, т.е. потенциальные.
Почему слушающий поток не всегда может установить соединение и выделить клиенту отдельный поток, чтобы эти неразрешённые соединения накапливались...
4. Я унаследовал класс TTCPServer от TIdTCPServer. Далее пытаюсь проинициализировать обработчики событий:
inherited OnExecute := onexecute;
nherited OnConnect := onconnect;
inherited OnDisconnect := ondisconnect;
Почему после наследования приходится приписывать к ним inherited.
Без них - ошибка.
Разыменование здесь нужно потому что переменная buffer объявлена именно как pointer, в то время как соотв.форм.параметр объявлен как нетипизированный и передаваемый по ссылке, а не по значению.
Вообще странно, что допускается разыменование нетипизированного указателя... В C++ так делать нельзя...
12. Чем отличается тип Pointer от PVariant ?
13. И почему в сигнатуре методов пишут var имя_парам, а не var Vaiant. Т.е., фактически, пропускают тип параметра...
Сергей М. (2006-09-22 08:17:00) [58]
> Вызов происходит в OnExecute соответствующего дочернего
> потока. При этом не допускается одновременных выполнений
> этой процедуры в нескольких потоках
Ну и нафиг тогда тебе Инди со всеми его наворотами ?
Индейский сервер изначальноподразумевает его использование для параллельнойобработки клиентских запросов.
Бзял бы обычный TServersocket или TTCPServer в режиме non-blocking - там события возбуждаются последовательно в одном-единственном потоке (например, в основном), и никакие заморочки с крит.секциями и прочей синхронизацией изобретать совершенно не нужно.
> synchronize(метод без парам). Но здесь возникает проблема:
> процедура у меня с параметрами. Как ето самое уделать ?
>
Вот так:
TMyThread = class(TSomeThreadAncestor)
..
FSomeParam: SomeType;
..
procedure SomeSyncMethod;
procedure SomeProc(SomeParam: SomeType);
end;
....
procedure TMyThread.SomeSyncMethod;
begin
SomeProc(FSomeParam);
end;
...
FSomeParam := SomeValue;
Syncronize(SomeSyncMethod);
Cyrax (2006-09-21 23:04:00) [59]У меня механизм обработки входных данных следующий.
При инициализации клиента/сервера указывается процедура, которая будет вызываться, когда приходят данные. Вызов происходит в OnExecute соответствующего дочернего потока. При этом не допускается одновременных выполнений этой процедуры в нескольких потоках. Т.е. процедура должна полностью завершиться до начала следующего её вызова.
Имеем 2 варианта реализации вызова:
1. Делаем что-то типа lock'ов для чего-то...
2. Вызываем эту процедуру на выполнение в главный (первичный) поток с помощью synchronize(метод без парам). Но здесь возникает проблема: процедура у меня с параметрами. Как ето самое уделать ?
з.ыз. В случае 2 как раз и будет неявно организована очередь обработки запросов клиентов.
Сергей М. (2006-09-20 14:08:00) [60]По поводу Free ..
Если ты имеешь
TMyClass = class(...)
...
destructor Destroy; override;
end;
TMyClass1 = class(TMyClass)
...
destructor Destroy; override;
end;
TMyClass2 = class(TMyClass1)
...
destructor Destroy; override;
end;
и создаешь объект класса TMyClass2
var MyObject: TMyClass;
MyObject := TMyClass 2.Create(..);
то при вызове MyObject.Free (или непосредственно MyObject.Destroy, что несущественно в дан.случае) самым первымбудет вызван метод TMyClass2.Destroy, поскольку этот метод перекрывает соотв.виртуальный метод предка, который точно так же перекрывает соотв.виртуальный метод своего предка, который... и так далее вплоть до TObject.Destroy, который собственно и объявлен как виртуальный метод, допускающий перекрытие (override) в своих потомках.