Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Проблемы при работе с Indy [D7, WinXP]


Страницы:   1 2 3 4 5 6 7 8

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-09-20 11:05:00) [61]


> в моём классе не объявлен Free, поэтому, вызывается Free
> базового класса, который ну просто никак не обязан шерстить
> своих потомков в поисках Destroy


Обязан.
Потому что Destroy - виртуальный метод.


> Например, для информирования сервером оператора


Зачем серверу оператор ? Зачем оператору инф-ция об отверженном соединении ?


> Я так не думаю


Ну тогда продолжай в том же духе.


> Такие вопросы кодом сложно


Ты считаешь более простым объяснять тебе на пальцах неизвестно что и неизвестно почему происходящее в неизвестно каком коде ?


> Какие соединения относятся к неразрешённым


Те которые ждут акцептирования, т.е. потенциальные.


> Мне же нужно для операций чтения


Тогда см. св-во TIdTCPConnection.ReadTimeout


> Фрагмент корректно работающего кода:
> ...
> buffer: Pointer;
> ...
> AThread.Connection.ReadBuffer(buffer^,data_len);


Разыменование здесь нужно потому что переменная buffer объявлена именно как pointer, в то время как соотв.форм.параметр объявлен как нетипизированный и передаваемый по ссылке, а не по значению.

Конструкция buffer^ в этом случае означает предписание компилятору сгенерировать код, передающий фактическим параметром не адрес переменной buffer, а адрес, хранящийся в этой переменной.

Вот если бы ты объявил, к примеру,
buffer: array[...] of sometype
то тогда разыменование не потребовалось бы, потому что по ссылке будет передана сама переменная, в которую будет осуществляться чтрение, что собственно и требуется.


Dmitrij_K   (2006-09-20 10:53:00) [62]


> Всё хорошо, только вот в режиме отладки в библиотечные функции
> не пускают..

Project-Options-Compiler- On Use Bebug DCUs


Cyrax   (2006-09-20 09:56:00) [63]

>Сергей М. ©   (19.09.06 08:53) [93]
>> Cyrax ©   (18.09.06 21:34) [92]
>> Ну да...
>Вот тебе и "ну да")

Я хочу сказать, что в моём классе не объявлен Free, поэтому, вызывается Free базового класса, который ну просто никак не обязан шерстить своих потомков в поисках Destroy...

>> Слепил свой класс TClientThreadQueue, унаследовав TQueue
>> и TThreadList. Получил комбайн высшего класса

Я провёл цептографическое наследование классов с генерацией fusion-объектов...
Так-что проблема отдельных списков решена...

> Как тогда зафиксировать факт такой попытки ?
А зачем ?

Например, для информирования сервером оператора...

> Почему, когда ты в производном классе определяешь конструктор
> и деструктор, при их вызове (а вернее, до и после, соответственно)
> автоматически не вызываются конструкторы и деструкторы базового
> класса ?
Потому что у тебя ошибка в декларации/реализации/использовании конструктора и деструктора "производного" класса (*)

Я так не думаю...

> Почему после наследования приходится приписывать к ним inherited.
Ну уж не может быть так, чтобы всё наследовалесь от TIdTCPServer, а
OnExecute'ы и т.п. - нет...

> 7.
> 8.
Такие вопросы кодом сложно.

> 9.
Что конкретно не понятно ?

Какие соединения относятся к неразрешённым.. %-|

> Функция CurrentReadBuffer вызывает ReadFromStack, если Connected.
>  Т.е. её поведение аналогично ReadFromStack. НО: вызывает
> она ReadFromStack с одним параметром - true
см. параметр метода TIdTCPClient.Connect()

Это не есть 2-й параметр ReadFromStack. И это только для initiate connection.
Мне же нужно для операций чтения/записи...

>> как можно получить данные по Pointer'у без приведения его
>> к какому-нибудь типизированному указателю
>Никак.

Фрагмент корректно работающего кода:
...
buffer: Pointer;
...
AThread.Connection.ReadBuffer(buffer^,data_len);
...

Речь идёт о безошибочности операции разыменования...

Dmitrij_K   (12.09.06 22:35) [86]
В Classes.pas ищи TCustomMemoryStream.Read

Всё хорошо, только вот в режиме отладки в библиотечные функции не пускают... В release-варианте, наверное, скомпилен (Delphi)...
Да ещё по Ctrl в ReadBuffer пускают, а дальше - в Read (это который от TCustomMemory) - никак...


Сергей М.   (2006-09-19 08:53:00) [64]


> Cyrax ©   (18.09.06 21:34) [92]


> Ну да...


Вот тебе и "ну да")


> Слепил свой класс TClientThreadQueue, унаследовав TQueue
> и TThreadList. Получил комбайн высшего класса


В Делфи у класса-наследник может быть только один класс-предок.
Думаю, тебя ждет шнобелевская премия, поскольку ты умудрился унаследовать аж два класса)
К тому же оба этих класса оперируют каждый своим отдельным списком)


> Как тогда зафиксировать факт такой попытки ?


А зачем ?


> Почему, когда ты в производном классе определяешь конструктор
> и деструктор, при их вызове (а вернее, до и после, соответственно)
> автоматически не вызываются конструкторы и деструкторы базового
> класса ?


Потому что у тебя ошибка в декларации/реализации/использовании конструктора и деструктора "производного" класса (*)


> Почему после наследования приходится приписывать к ним inherited.


См. (*)


> Как в Delphi перегрузить глобальную функцию ?


Вот так:

function SomeFunction(SomeParams): SomeResultType; overload;
function SomeFunction(Some OtherParams): SomeResultType; overload;


> 7.
> 8.


Ерунда какая-то ..
Иллюстрируй свою мысль в коде  ..


> 9.


Что конкретно не понятно ?


> Функция CurrentReadBuffer вызывает ReadFromStack, если Connected.
>  Т.е. её поведение аналогично ReadFromStack. НО: вызывает
> она ReadFromStack с одним параметром - true


см. параметр метода TIdTCPClient.Connect()


> как можно получить данные по Pointer'у без приведения его
> к какому-нибудь типизированному указателю


Никак.


> Free можно вызвать даже через объект (котороый является
> указателем, т.к. объекты - это указатели), который = nil.
> Парадокс...


Никакого парадокса.

Читай справку:

Every object inherits a Destroy method (called a destructor
) from TObject. To destroy an object, however, you should call the Free method (also inherited from TObject), because Free checks for a nil reference before calling Destroy


Cyrax   (2006-09-18 21:34:00) [65]

Сергей М. ©   (15.09.06 09:07) [91]
> в этом случае будет вызываться Free базового класса (TMemoryStream.
> Free), который о моих Destroy'ях даже и не догадывается
Еще как догадывается.

Ну да...

2. Слепил свой класс TClientThreadQueue, унаследовав TQueue и TThreadList. Получил комбайн высшего класса. :)

3 Клиент успешно подключится, но сервер втихомолку тут же сделает ему
disconnect, никаких событий и /или исключений сервер при этом не возбудит.

Как тогда зафиксировать факт такой попытки ?

4. Я унаследовал класс TTCPServer от TIdTCPServer. Далее пытаюсь проинициализировать обработчики событий:
inherited OnExecute      := onexecute;
nherited OnConnect      := onconnect;
inherited OnDisconnect   := ondisconnect;
Почему после наследования приходится приписывать к ним inherited.
Без них - ошибка.

5. Почему, когда ты в производном классе определяешь конструктор и деструктор, при их вызове (а вернее, до и после, соответственно) автоматически не вызываются конструкторы и деструкторы базового класса ?

6. Как в Delphi перегрузить глобальную функцию ?

7. При организации приёма/передачи данных на сервере с помощью входных и выходных (для каждого клиента) буферов возникает "проблема" получения результата операции, поскольку сервер отправляет в буфер выходные данные, а сам занимается другими делами. А OnExecute'ы занимаются обработкой входных и выходных буферов. Так вот, смущает тот факт, что на сервере мы не получаем оперативной информации о выполнении операции отправки/приёма, а на клиенте (там уже наших входных/выходных буферов нет, OnExecute'ов нет) мы эту информацию получаем сразу по исключению при выполнении метода TIdTCPClient.Connection.ReadBuffer.
Хотелось бы, чтобы всё-таки и на сервере, и на клиенте это дело одинаково было. Поскольку в модуле мне приходится объявлять константу ConnectionError (в составе перечислимого типа TNetResult), которая будет формироваться только клиентом, тогда когда сервер тоже имеет дело с соединениями...

8. Отключаю сервер при подсоединённом клиенте. Для всех клиентов вызываются disconnect'ы. Тем не менее после вызова этих disconnect'ов генерируется исключение (именно из-за disconnected, 1-й параметр - true) на ReadFromStack (сейчас я эту ф-цию не использую, но вопрос стоит) в OnExecute, хотя в OnExecute вначале у меня стоит проверка на Connected.
Если OnDisconnect вызывается в том же потоке (т.е. одно вызывается после другого), что и OnExecute (что и должно быть), то как можно проскочить проверку на Connected вначале OnExecute ?

9. Не совсем понятен смысл свойства ListenQueue объекта класса TIdTCPServer (а именно "unresolved connections").

10. Задача такова. Нужно считать из стека TCP данные без блокировки. Если входных данных нет - не ждать пока они придут, вернуть 0. Если есть - вернуть их количество.
Так вот, ReadFromStack, если на входе нет данных, ждёт пока не придёт хотя бы 1 байт, после возвращает их количество. Можно задать timeout = 0, тогда сразу вернёт 0, если на входе нет данных.
Функция CurrentReadBuffer вызывает ReadFromStack, если Connected. Т.е. её поведение аналогично ReadFromStack. НО: вызывает она ReadFromStack с одним параметром - true. Т.е. 2-й параметр по ум. = -1 (ждать вечность, пока не придёт хотя бы 1 байт). Получается, если использовать функцию CurrentReadBuffer, то, если на входе нет ни байта, то будем ждать вечность. Как этого избежать, не используя ReadFromStack с нулевым вторым параметром.

11. Не могу понять, как можно получить данные по Pointer'у без приведения его к какому-нибудь типизированному указателю:
buffer^, где buffer типа Pointer...

12. Free можно вызвать даже через объект (котороый является указателем, т.к. объекты - это указатели), который = nil.
Парадокс...


Сергей М.   (2006-09-15 09:07:00) [66]


> Cyrax ©   (14.09.06 23:14) [90]


1. Правильно сделал, что "понаставил .. Free".


> в этом случае будет вызываться Free базового класса (TMemoryStream.
> Free), который о моих Destroy'ях даже и не догадывается


Еще как догадывается.

2. TThreadList вполне подходит для организации очереди.
А если уж удобней использовать T[Object]Queue, придется организовать защиту обращений к очереди с пом. крит.секции (сделать класс TThread[Object]Queue по аналогии с TThreadList совсем не трудно)


> Что произойдёт, если попытается подключиться 257-й клиент
> ?


Клиент успешно подключится, но сервер втихомолку тут же сделает ему
disconnect, никаких событий и /или исключений сервер при этом не возбудит.


> 4. В дочернем классе объявлен метод, одноимённый с методом
> родительского класса (ReadBuffer, WriteBuffer) без перегрузки.
>  Как вызвать метод родительского класса из дочернего ?


К чему создавать себе трудности ?
Есть методы Read/Write, они виртуальные. Перекрой их в своем наследнике, тогда соотв.методы предка легко вызываются с пом. inherited.
Методы же ReadBuffer/WriteBuffer не виртуальные, и все что они делают - вызывают соответствующие вирт.методы Read/Write.


> 6. Не совсем понятно поведение программы при попытке получения
> доступа к списку другим потоком


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


Cyrax   (2006-09-14 23:14:00) [67]

1. Насчёт Free. Что-то сразу не подумал и понаставил для своих классов Free вместо Destroy. Естественно, в этом случае будет вызываться Free базового класса (TMemoryStream.Free), который о моих Destroy'ях даже и не догадывается...

2. Очередь TQueue. Довольно тонкий вопрос совмещения очереди Queue (типа TQueue или TObjectQueue) и разделяемого потоками списка ThreadList (типа TThreadList), содержащего указатели на данные клиентов.
    Дело в том, что если взять очередь указателей (TQueue), то воле-неволей попытаешься направить их на те же участки памяти, куда указывают элементы списка ThreadList. Но в момент доступа к данным клиентов с помощью этой очереди никаких блокировок не происходит (как в случае доступа через TThreadList.LockList.Items...). Т.е. этот вариант отпадает из-за невозможности заблокировать ThreadList (доступ к клиентским данным осуществляется, минуя его)...
    Если же взять очередь объектов (TObjectQueue), то единственно эффективным вариантом будет хранить в этой очереди, например, идентификаторы (целое число) клиентов (сейчас я так и сделал), которые являются одним из полей структуры, содержащей клиентские данные.
    Минусом такой организации очереди является необходимость при каждом очередном доступе к какому-либо клиенту по полученному из очереди идентификатору искать соответствующие данные по списку ThreadList.
    Другой минус - необходимость синхронизации очереди ObjectQueue и списка ThreadList при каждом соединении/отсоединении клиента.
    Плюс очереди объектов - каждая операция доступа к клиентским данным происходит через список ThreadList, что влечёт за собой обязательную блокировку списка.

3. Требуется ограничить число одновременно подключенных клиентов (const ClientsMax). Подключение обрабатывается в OnConnect, т.е. ничего передать в использующую модуль программу не получится. Поле ListenQueue сервера (IdTCPServer) - как раз для этого. Допустим, я установлю его в 256. Что произойдёт, если попытается подключиться 257-й клиент ?  Полный игнор ?  Не вызовется даже OnConnect ?  Или возникнет исключение (если так, то где, в каком месте) ?  В любом случае мне нужно зафиксировать такую попытку соединения, чтобы сообщить при первой возможности использующей модуль программе.

4. В дочернем классе объявлен метод, одноимённый с методом родительского класса (ReadBuffer, WriteBuffer) без перегрузки. Как вызвать метод родительского класса из дочернего ?

5. Не совсем удобно получается с поиском или перебором клиентских данных с помощью цикла for. Дело в том, что в заголовке цикла указывается верхний предел, который получаем из ThreadList.LockList.Count. Но здесь происходит блокировка списка. Затем в теле цикла осуществляется доступ к клиентским данным (ThreadList.LockList.Items...). Каждая такая операция сопровождается очередной блокировкой. Сколько таких блокировок произойдёт внутри цикла - заранее неизвестно (придётся столько же раз разблокировывать). Единственный выход из ситуации - загнать в локальную переменную величину ThreadList.LockList.Count, разблокировать список (через finally), записать заголовок цикла с помощью этой локальной переменной, а в теле цикла каждую операцию доступа к списку ThreadList завершать (через finally) UnLock'ом.
При этом происходит загромождение кода этими try'ами и finally'ами (от 2 пар на 1 цикл for). Гораздо эффективнее было бы, например, если бы имелась возможность получать доступ к элементам списка неблокирующим способом (ну можно, конечно, дополнительно хранить указатель на список, получаемый через ThreadList.LockList.Items..., но это уже слишком). Тогда достаточно было бы заблокировать список (а точнее, объект ThreadList) перед циклом и разблокировать после него. А в заголовке и в теле цикла использовать неблокирующие операции доступа к списку.

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

____________________________________________________________________
Сергей М. ©   (14.09.06 09:34) [89]
За образец класса, реализующего два независимых позиционных стрим-маркера (тек.позиция записи и тек.позиция чтения) можно было взять класс TDataBlock (sconnect.pas).

Да, можно и так. Но всё же оставлю прежний вариант с одним указателем.
(Всё равно придётся самому перемещать указатели)

Следующие высказывания эквивалентны по смыслу:
Содержимое переменной (параметра) типа pointer
a) содержит адрес неких данных некоего типа.
b) указывает на некие данные некоего типа
с) ссылается на некие данные некоего типа

Если в последнем пункте под словом "ссылается" понимается не ссылка на
данные, а указатель, то да, соглашусь.
Моё ИМХО:
В Object Pascal, как и в C, ссылки и указатели различаются следующим образом. По поведению в программе значением ссылки являются данные, а указателя - адрес, хотя на уровне реализации и те, и другие представлены адресами данных, на которые ссылаются/указывают.

См. выше. В том и прелесть Паскаля, что его строгость не допускает никаких неоднозначных трактовок, что сводит к минимуму ошибки, являющиеся следствием таких неоднозначностей (в Сях они сплошь и рядом).

Ну, в C компилятор тоже выдаст ошибку, если вместо указателя передать данные (имя массива там не относится к таким данным). А неоднозначностью передачу имени массива я не считаю, поскольку там нет таких случаев, где бы имя массива интерпретировывалось не как указатель на первый его элемент... :)

Кстати, понравилась такая запись нескольких действий в одну строку:

InputBuffer := get_client(Integer(id_queue.push(id_queue.pop))).InputBuffer;

(Сильно напоминает C++...)

____________________________________________________________________
Dmitrij_K   (12.09.06 22:35) [86]
В Classes.pas ищи TCustomMemoryStream.Read

Всё хорошо, только вот в режиме отладки в библиотечные функции не пускают... В release-варианте, наверное, скомпилен (Delphi)...
Да ещё по Ctrl в ReadBuffer пускают, а дальше - в Read (это который от TCustomMemory) - никак...


Сергей М.   (2006-09-14 09:34:00) [68]


> Cyrax ©   (13.09.06 22:11) [88]


> Это не буфер


Разумеется.
Это то самое значение, которое ты читаешь из InputBuffer.Size.
Так вот этот самый InputBuffer и есть ничто иное как "вспомогательный буфер", аккумулирующий принимаемый из гнезда вх.поток данных.


> Мне нужен был поток, который бы при чтении декрементировал
> position и уменьшал размер. Подобно Connection'у. Оных не
> нашёл - пришлось наследовать самому


Нормальное решение.
За образец класса, реализующего два независимых позиционных стрим-маркера (тек.позиция записи и тек.позиция чтения) можно было взять класс TDataBlock (sconnect.pas).


> Адрес или указатель на этот адрес ?


Данные указ.типа (pointer) предназначены для хранения адресов.

Следующие высказывания эквивалентны по смыслу:
Содержимое переменной (параметра) типа pointer
a) содержит адрес неких данных некоего типа.
b) указывает на некие данные некоего типа
с) ссылается на некие данные некоего типа


> data здесь воспринимается не как указатель на первый элемент
> массива (как в C), а как сам массив, адрес которого ещё
> нужно вычислить ?


Да, верно.

Для вычисления адреса переменной data в дан.случае как раз и применяется префикс @. Вычисленный таким образом адрес трактуется компилятором как данные указ.типа, что не противоречит типу форм.параметра, объявленного как pointer.


> Предположим, я передам data там, где требуется указатель
> на адрес (pointer)


Ты его попросту не передашь. Компилятор выдаст ошибку "несоответствие типов формального и фактического параметров", ибо формально ожидается адрес данных, а фактически передаются сами данные.


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


Нет, не возьмет. См. выше. В том и прелесть Паскаля, что его строгость не допускает никаких неоднозначных трактовок, что сводит к минимуму ошибки, являющиеся следствием таких неоднозначностей (в Сях они сплошь и рядом).


> длина статического массива хранится в байтах перед первым
> элементом массива ?


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


Cyrax   (2006-09-13 22:11:00) [69]

Dmitrij_K   (12.09.06 22:35) [86]
В Classes.pas ищи TCustomMemoryStream.Read

Всё чих-пых, только вот в режиме отладки в библиотечные функции не пускают... В release-варианте, наверное, скомпилен (Delphi)...
Да ещё по Ctrl в ReadBuffer пускают, а дальше - в Read (это который от TCustomMemory) - никак...

Блин, я везде в своих классах Destroy вызывал...
( The Destroy destructor should never be called directly. To destroy a component created with Create, call the Free method.)

Сергей М. ©   (13.09.06 09:09) [87]
>> Это же и есть без вспомогательного
>А SomeSize тебе с потолка свалился ?

Это не буфер.

Чтение из стрима всегда осуществляется с тек.позиции в стриме.
А она у тебя в момент попытки чтения указывает на конец стрима, оттого и исключение.

Всё понятно. Мне нужен был поток, который бы при чтении декрементировал position и уменьшал размер. Подобно Connection'у. Оных не нашёл - пришлось наследовать самому...

> почему @data[0], а не data ?
Потому что формальный параметр Destination объявлен как pointer и тербует передачи в кач-ве фактического параметра нач.адреса блока памяти...

Адрес или указатель на этот адрес ?
Я думаю указатель (нетипизированный) на этот адрес (pointer).

Вот если бы Destination был объявлен как нетипизированный параметр, передаваемый по ссылке (т.е. var Destination или const Destination), факт.параметр data можно было бы передать так как ты привык на Сях:
        ZeroMemory(data, sizeof(data));
В этом случае заботу о вычислении нач.адреса блока памяти, распределенного под факт.параметр, берет на себя сам компилятор.

Т.е. data здесь воспринимается не как указатель на первый элемент массива (как в C), а как сам массив, адрес которого ещё нужно вычислить ?

Предположим, я передам data там, где требуется указатель на адрес (pointer). Тогда компилятор возмёт первые 4 байта массива и интерпретирует их как адрес данных, с которыми он будет работать ?

_____________________________________________________________
Кстати, длина статического массива хранится в байтах перед первым элементом массива ?  И сколько байт выделяется под эту длину ?


Сергей М.   (2006-09-13 09:09:00) [70]


> Cyrax ©   (12.09.06 21:44) [83]


> только передавать его можно не по всякому


Разумеется не по-всякому.
Это зависит от того как декларированы формальный и фактический параметры.


> ВАП - это типа процессоптера ?


Это Виртуальное Адресное Пространство (процесса).


> Это же и есть без вспомогательного


А SomeSize тебе с потолка свалился ?


> есть ли в Delphi модули для работы с динамическими очередями
> - типа C'ных queue.h ?
> Нужно для целых.


Есть.
См. класс TQueue в Contnrs.pas


>  buf.WriteBuffer(data,5);  // всё чисто: size = 5, position
> = 5
>  buf.ReadBuffer(data,5);  // ошибка: stream read error


Чтение из стрима всегда осуществляется с тек.позиции в стриме.
А она у тебя в момент попытки чтения указывает на конец стрима, оттого и исключение.


> В Read просунуться не смог - нет исходников


Есть исходники !

cм. TCustomMemoryStream.Read (Classes.pas)


> я на сях привык.
> А там data - указатель на data[0]


И в данном случае тоже самое - 1-м параметром в ReadBuffer() фактически передается адрес data[0]


> почему @data[0], а не data ?


Потому что формальный параметр Destination объявлен как pointer и тербует передачи в кач-ве фактического параметра нач.адреса блока памяти.
А нач.адрес стат.массива data и есть адрес его младшего элемента, т.е. @data[0]

Вот если бы Destination был объявлен как нетипизированный параметр, передаваемый по ссылке (т.е. var Destination или const Destination), факт.параметр data можно было бы передать так как ты привык на Сях:

ZeroMemory(data, sizeof(data));

В этом случае заботу о вычислении нач.адреса блока памяти, распределенного под факт.параметр, берет на себя сам компилятор.


Dmitrij_K   (2006-09-12 22:35:00) [71]


> Это я на сях привык.

Тогда простительно :)
ZeroMemory и sleep для проверки данных в отладчике, мусор вобщем
В Classes.pas ищи TCustomMemoryStream.Read

> И почему Free, а не Destroy ?

Так принято в Delphi.
Ошибка в Read потому что ты пытался читать с позиции 5, те вверх а не вниз. Вот и получил AV


Cyrax   (2006-09-12 22:17:00) [72]

Я не идиот !  Это я на сях привык.
А там data - указатель на data[0].
Деструктор - не Free, а что-то вроде Destroy.

ZeroMemory(@data[0], SizeOf(data));

Если это чистка памяти, то почему @data[0], а не data ?

buf.Position := 0;

Create - что, Position не обнуляет ?

Кроме того, с connection'ом мой вариант кода прокатывает без проблем !
А finally и sizeof - не использовал, поскольку фрагмент не из моего модуля, а надуманный, только для анализа етого фокуса...
И почему Free, а не Destroy ?  Деструктор же. Не должен тупить...

if data[0]=1 then
    sleep(1);

А ето шо ?  Особенно sleep ?

И чем мой код принципиально отличается от етого. Глюки были только из-за ZeroMemory ?  Не верится...

Не надо врать. Classes.pas должен быть

Я и не вру. Classes.pas есть. Но там только ReadBuffer имплементится, Read - нет. А дальше (в Read) - не пускают...


Dmitrij_K   (2006-09-12 21:54:00) [73]


> Просто приведу фрагмент кода

Смотрел плакал
 
 buf := TMemoryStream.Create;
 try
   buf.WriteBuffer(data,5);
   ZeroMemory(@data[0], SizeOf(data));
   buf.Position := 0;
   buf.ReadBuffer(data,5);
   if data[0]=1 then
     sleep(1);
 finally
   buf.Free;
 end



> В Read просунуться не смог - нет исходников

Не надо врать. Classes.pas должен быть


Cyrax   (2006-09-12 21:44:00) [74]

Ну ты бы хоть в исходники заглянул, прежде чем рассуждать)...

Да.. Рассказал сказок...

Буфером может быть любой блок памяти, так или иначе распределенный в ВАП тек.процесса и доступный по записи.

... только передавать его можно не по всякому...
А ВАП - это типа процессоптера ?

MemStream.Size := SomeSize;
Connection.ReadBuffer(MemStream.Memory^, MemStream.Size);

Это же и есть без вспомогательного... (из Connection в MemoryStream)

___________________________
А есть ли в Delphi модули для работы с динамическими очередями - типа C'ных queue.h ?
Нужно для целых.

__________________________
А теперь про TMemoryStream.
Материться не буду. Просто приведу фрагмент кода:

var buf: TMemoryStream;
    int i: integer;
    data: array[0..99] of byte;
begin
 for i := 1 to 99 do data[i] := i;
 buf := TMemoryStream.Create;
 buf.WriteBuffer(data,5);  // всё чисто: size = 5, position = 5
 buf.ReadBuffer(data,5);  // ошибка: stream read error (чтение по такому-то адресу)
                                 // ReadBuffer вызывает Read, который возвращает 0 (число прочитанных байтов),
                                 // из-за чего (count <> 0) генерится ошибка. В Read просунуться не смог - нет исходников...
                                 // Пробовал после создания buf Clear'ить, обнулять Position - то же самое. Пробовал с TStringStream - ошибки нет, но читает 0 символов (т.е. ReadString возвращает '').
 buf.Destroy;
end;

... а дальше можно вешаться... или пристрелиться...


Сергей М.   (2006-09-12 08:33:00) [75]


> буфера, с которыми работает Connection, - это вовсе не InputBuffer.
>  Причём InputBuffer - только для удобства вывода.
> И для работы с InputBuffer его нужно проинициализировать
> (кажется, Connection.CreateBuffer или что-то типа того).
>


Ну ты бы хоть в исходники заглянул, прежде чем рассуждать)...


> string в Delphi - не объект ?


Нет, не объект.


> первым параметром WriteBuffer'а может быть чистый адрес?


Что значит "чистый" ?


> 1. Что за буфер нужен методу ReadBuffer класса TMemoryStream
> (первый параметр)?


Буфером может быть любой блок памяти, так или иначе распределенный в ВАП тек.процесса и доступный по записи.


> Как в Connection.ReadBuffer не прокатывает


Что значит "не прокатывает" ?

Прототипы методов обоих классов абсолютно идентичны.


> у меня массив byte'ов


Любой блок памяти, используемый как буфер, можно интерпретировать как массив байт.


> Передаю имя массива


Массив при этом д.б. статический.


> мне прочитать в TMemoryStream. Пытаюсь как-то с MemoryStream.
> Memory, но ни в какую


MemStream.Size := SomeSize;
Connection.ReadBuffer(MemStream.Memory^, MemStream.Size);


> 2. Как можно без вспомогательного локального буфера считать
> из Connection в MemoryStream ?


Никак.
Промежуточный буфер так или иначе (явно или неявно) используется, без него никак не обойтись.


Cyrax   (2006-09-11 20:07:00) [76]

Что значит "задействовать" ? Он и так задействован в TIdTCPConnection, хочешь тыт того или нет...

(типа "да-да", "ну-ну" не буду...:) )
По моему, буфера, с которыми работает Connection, - это вовсе не InputBuffer. Причём InputBuffer - только для удобства вывода.
И для работы с InputBuffer его нужно проинициализировать (кажется, Connection.CreateBuffer или что-то типа того).

s := AThread.Connection.CurrentReadBuffer;
if Length(s) > 0 then
AThread.Connection.WriteBuffer(PChar(s)^, Length(s));

А string в Delphi - не объект ?.. чтобы его PChar'ить...
А первым параметром WriteBuffer'а может быть чистый адрес?

И вот ещё два таких вопроса возникло.
1. Что за буфер нужен методу ReadBuffer класса TMemoryStream (первый параметр)? Как в Connection.ReadBuffer не прокатывает. А там у меня массив byte'ов. Передаю имя массива. Pointer проглатывает.
1.5. А нужно мне прочитать в TMemoryStream. Пытаюсь как-то с MemoryStream.Memory, но ни в какую...
2. Как можно без вспомогательного локального буфера считать из Connection в MemoryStream ?


Сергей М.   (2006-09-11 10:21:00) [77]


> Только для этого задействовать буфер..


Что значит "задействовать" ? Он и так задействован в TIdTCPConnection, хочешь тыт того или нет...


> Затем преобразовывать строку в цепочку байтов - неэффективно.
>
> Да и внутренний буфер опять задействовать придётся...


Это еще зачем ?

s := AThread.Connection.CurrentReadBuffer;
if Length(s) > 0 then
 AThread.Connection.WriteBuffer(PChar(s)^, Length(s));


Cyrax   (2006-09-11 10:15:00) [78]

Connection.InputBuffer.Size

Только для этого задействовать буфер...

Connection.CurrentReadBuffer

Затем преобразовывать строку в цепочку байтов - неэффективно.
Да и внутренний буфер опять задействовать придётся...


Сергей М.   (2006-09-11 09:59:00) [79]

Вру.

См. св-во Connection.InputBuffer.Size

Кр.того есть метод Connection.CurrentReadBuffer - он вернет строку, длина которой и показывает "сколько их там, байтов"


Сергей М.   (2006-09-11 09:51:00) [80]


> Cyrax ©   (11.09.06 09:37) [76]


> Чтобы не блокировать поток, поскольку в OnExecute происходит
> не только чтение, но и отправка


Блокирование происходит не более чем на время, указанное в таймаут-параметре.


> Вот это и нужно знать - сколько их там, байтов


Объект TIdTCPConnection имеет св-во RecvBufferSize - это и есть размер данных, доступных для чтения в дан.момент.


Страницы:   1 2 3 4 5 6 7 8

Наверх

Титульная страница Поиск, карта сайта Написать письмо