Форум: "Начинающим";
Текущий архив: 2006.11.12;
Скачать: [xml.tar.bz2];
ВнизПроблемы при работе с Indy Найти похожие ветки
← →
Cyrax © (2006-09-04 10:14) [40]Ну и делай)
Ну а варианты то какие ? Например, с OnExecute.
в чем проблема-то
Да мне нужно обнаружить отсоединение клиента как можно раньше, хотя бы в момент чтения/записи. Но если это делать из первичного потока, то исключений, вызывающих OnDisconnecting, не вырабатывается...
← →
Cyrax © (2006-09-04 10:27) [41]Сергей М. © (04.09.06 10:14) [39]
Вообще-то примерчик неплох... стоит попробовать.
А как насчёт освобождения порта ?
← →
Сергей М. © (2006-09-04 10:30) [42]
> мне нужно обнаружить отсоединение клиента как можно раньше,
> хотя бы в момент чтения/записи
см. [23]
> как насчёт освобождения порта ?
см. [21]
← →
Cyrax © (2006-09-04 10:37) [43]По поводу [23]:
Обработчик OnDisconnect вызывается стандартным обработчиком исключения (см. [31])... (по крайней мере, у меня так работало, без своих except"ов).
> см. [21]
Как насчёт [32]?
← →
Сергей М. © (2006-09-04 10:50) [44]
> Cyrax © (04.09.06 10:37) [43]
Ты ведь спросил, как оперативно отреагировать на отключение клиента в момент выполнения операций приема/передачи, я тебе и ответил - заключать вызов таких ф-ций в try..except
Если возникло исключение, то оно будет при этом перехвачено и обработано тобой должным образом.
При чем здесь OnDisconnect ?
> Как насчёт [32]?
А что насчет него ?
Я тебе уже сказал - нет никакой необходимости знать сколько байт доступно во входном потоке.
Сколько требуется прочитать в соответствии с твоим прикладным протоколом инф.обмена, столько и читай.
← →
Cyrax © (2006-09-04 11:08) [45]Если возникло исключение, то оно будет при этом перехвачено и обработано тобой должным образом.
При чем здесь OnDisconnect ?
Хочу сказать? что я обрабатываю это исключение в обработчике OnDisconnect. А OnDisconnect у меня вызывается стандартным обработчиком этого исключения, даже если не отлавливать try...except"ом...
Протокол у меня подразумевает вначале обязательную передачу размера пакета (моего, прикладного протокола), а затем сами данные. Это нужно для того, чтобы считать из сокета не все данные (а там может находится и несколько прикладных пакетов и даже нецелое их число), а только один мой пакет сообщения.
> Как насчёт [32]?
А вообще, я имел ввиду освобождение порта клиентом...
Там мой пример, где как раз и использую твой код...
← →
Сергей М. © (2006-09-04 11:21) [46]
> я обрабатываю это исключение в обработчике OnDisconnect.
> А OnDisconnect у меня вызывается стандартным обработчиком
> этого исключения, даже если не отлавливать try...except"ом
Ну можно и так.
> мой пример, где как раз и использую твой код
Приведи дословно текст сообщения об исключении, возникающем при выполнении IdTCPClient1.Connect
← →
Cyrax © (2006-09-04 11:28) [47]
Socket Error # 10048
Address already in use.
Даже после перезагрузки клиента и сервера при соединении клиента сразу же возникает это исключение (с тем же портом клиента)...
← →
Сергей М. © (2006-09-04 11:35) [48]
> Cyrax © (04.09.06 11:28) [47]
Повторил твой код с максимальной точностью
object IdTCPClient1: TIdTCPClient
...
BoundPort = 55559
Host = "127.0.0.1"
...
end
Никаких "Address already in use" по-прежнему не наблюдаю, хоть без хоть с перезагрузкой клиента.
← →
Cyrax © (2006-09-04 11:42) [49]Что за полтергейст... средь бела дня...
А ты пробовал создать файлы с текстом из [32] и скомпилить ?
P.S. Знаю, страшно ломает...
← →
Сергей М. © (2006-09-04 11:47) [50]
> ты пробовал создать файлы с текстом из [32] и скомпилить
> ?
>
А нафига ?
Если ты утверждаешь, что именно строка IdTCPClient1.Connect приводит к отказу 10048, то сервер тут вообще ни при чем .. Зачем, спрашивается его компилить ?)
← →
Cyrax © (2006-09-04 11:49) [51]Глюко всяко случается...
← →
Сергей М. © (2006-09-04 11:52) [52]
> Cyrax © (04.09.06 11:49) [51]
Возьми любую готовую утилитку (а-ля TCPView) да посмотри, занят ли порт 55559 после первого же дисконнекта клиента.
← →
Cyrax © (2006-09-05 09:37) [53]Всё, запарился... Не буду извращаться с Indy...
Сделаю в соответствии с идеологией Indy - все read/write в OnExecute и потоки в OnExecute. А то OnDisconnect достал...
Попутно соображаю по поводу того, как лучше организовать implement моих фунций чтения/записи. Нужно, чтобы эти функции сразу же возвращали результат (код ошибки), а для этого нужно подождать, пока выполниться операция чтения/записи в соответствующем потоке.
Остановился пока на варианте ждать в цикле, пока не завершится операция в потоке (с помощью флагов), а затем результат операции возвращать в главную программу...
Что касается порта клиента, то проверил OutPost"ом - порт все-таки освобождается... но ошибка выходит... Может, руки кривые...
Проверю ещё раз, где именно вылазит.
← →
Сергей М. © (2006-09-05 10:06) [54]
> Нужно, чтобы эти функции сразу же возвращали результат (код
> ошибки), а для этого нужно подождать, пока выполниться операция
> чтения/записи в соответствующем потоке
При этом теряется весь смысл выполнения трансп.операций в доп.потоке.
> Остановился пока на варианте ждать в цикле, пока не завершится
> операция в потоке
Плохой вариант.
← →
Cyrax © (2006-09-05 10:12) [55]Согласен, но по-другому не получится...
← →
Сергей М. © (2006-09-05 11:23) [56]
> по-другому не получится.
Чтой-то вдруг ?
← →
Cyrax © (2006-09-05 12:33) [57]Да-да...
← →
Сергей М. © (2006-09-05 12:53) [58]
> Да-да
Ну-ну)
← →
Cyrax © (2006-09-05 13:06) [59]:)...
lol
← →
Cyrax © (2006-09-06 12:50) [60]Сергей М. © (05.09.06 10:06) [54]
Я:
> Нужно, чтобы эти функции сразу же возвращали результат (код
> ошибки), а для этого нужно подождать, пока выполниться операция
> чтения/записи в соответствующем потоке
> Остановился пока на варианте ждать в цикле, пока не завершится
> операция в потоке
Плохой вариант.
если ты это серьёзно, то не подскажешь другой...
← →
Сергей М. © (2006-09-06 12:59) [61]
> Cyrax © (06.09.06 12:50) [60]
Зачем основному потоку ждать результат отправки сообщения ?
Зачем основному потоку ждать приема сообщения ?
Ему что, нечем более заняться ?
← →
Cyrax © (2006-09-06 22:50) [62]Да это всё начальство. Так что подумать придётся...
Впрочем, можно его и переубедить. Главное - убедительно аргументировать...
Раньше у них был модуль, работающий через IPX. Там функции чтения/записи возвращали результат:
TBrsNetResult = (OK,CRCERROR,TIMEOUT,BUSY,SYSTEMERROR,TERMINATED,CHANNELERROR,SEQUENCEERROR,CFER ROR);
В общем так получается. Ты следишь только за отключением клиента (или разрывом связи) в OnDisconnect... Всё остальное - на совести TCP. (если там что-то случится, получишь OnDisconnect).
________________________
Ещё вот такой глюк у меня:
procedure TTCPServer.onconnect(AThread: TIdPeerThread);
...
begin
...
temp_ptr_1 := PTSysClient(AThread.Data);
temp_client_1 := temp_ptr_1^;
end;
procedure TTCPServer.ondisconnect(AThread: TIdPeerThread);
...
begin
temp_ptr_2 := PTSysClient(AThread.Data);
temp_client_2 := temp_ptr_2^;
...
end;
Здесь TSysClient - класс с данными о клиенте. PTSysClient - указатель на такой класс. temp_ptr_1/2 типа PTSysClient, temp_client_1/2 - TSysClient. Все они глобальные. AThread.Data у меня указывает на объект класса TSysClient.
Так вот. Когда клиент соединяется, в temp_client_2 получаю то что ожидается. НО: когда клиент отсоединяется, то в temp_client_2 получаю козяблики вместо класса. Ощущение, что temp_ptr_2 указывает на левую область памяти. И в то же время (проверил) AThread тот же самый, AThread.Data = $то_же_самое (кстати, это значение указателя ?).
Если Data"ы указывают на одну и ту же область памяти и если по значению указателя temp_ptr_1 в момент выполнения OnDisconnect лежит клиент (никуда он не девается), то как по такому же указателю он может отсутствовать ?
Что за аномалия ?
То же самое наблюдается и в OnExecute.
____________________________________________
И насчёт порта клиента, который освобождается:
Сообщение выходит именно на connection"е. Порт при этом освобождается...
______________________________
Dmitrij_K (03.09.06 21:19) [35]
ioctlsocket(IdTCPClient1.Socket.Binding.Handle, FIONREAD, j);
в j кол-во принятых байт но еще не прочитанных из сокета
Что это за функция. Из какой библиотеки ?
← →
Dmitrij_K (2006-09-06 23:08) [63]
> ioctlsocket(IdTCPClient1.Socket.Binding.Handle, FIONREAD,
> j);в j кол-во принятых байт но еще не прочитанных из сокетаЧто
> это за функция. Из какой библиотеки ?
ws2_32.dll
← →
Сергей М. © (2006-09-07 08:37) [64]
> Cyrax © (06.09.06 22:50) [62]
> TSysClient - класс с данными о клиенте. PTSysClient - указатель
> на такой класс.
Ну не класс, наверное, а экземпляр (объект) класса TSysClient.
Объекты в Делфи и так уже представлены указателями, и к чему в таком случае хранить указатель на указатель - не понятно ..
> Когда клиент соединяется, в temp_client_2 получаю то что
> ожидается
А откуда там взялось это самое "то что ожидается" ? В какой момент времени, при каком событии ты устанавливаешь значение AThread.Data ?
> Все они глобальные
Это еще зачем ?
> AThread.Data = $то_же_самое (кстати, это значение указателя
> ?)
Ты про $ что ли ?
Нет , это не "значение указателя", это вообще непонятно что ..
Схема в твоем случае д.б. примерно такая:
В обработчике OnConnect:
var
SysClient: TSysClientl; //локальная переменная !
with SysClient do begin
.. здесь устанавливаешь св-ва объекта SysClient
end;
AThread.Data := SysClient; //ассоциируешь объект с транспортной нитью клиента
В обработчике OnDisconnect:
TSysClient(AThread.Data).Free; //уничтожаешь объект
В прочих обработчиках, где фигурирует AThread, обращаешься к объекту, ассоциированному с трансп.нитью:
with TSysClient(AThread.Data) do begin
SomeValue := .SomeProperty;
SomeMethod(SomeParams);
...
end;
← →
Сергей М. © (2006-09-07 09:33) [65]Извиняюсь, самое главное забыл:
var
SysClient: TSysClient; //локальная переменная !
..
SysClient := TSysClient.Create(..); //создаешь объект
with SysClient do begin
.. здесь устанавливаешь св-ва объекта SysClient
end;
AThread.Data := SysClient; //ассоциируешь объект с транспортной нитью клиента
← →
Cyrax © (2006-09-07 10:33) [66]> Ну не класс, наверное, а экземпляр (объект) класса TSysClient.
Угу...
> Объекты в Делфи и так уже представлены указателями, и к чему в таком
> случае хранить указатель на указатель - не понятно ..
Ухх... Это я от С так соображаю... (там TSysClient - не указатель).
Получается в делфе 2 типа указателей: указатель на объект, который реализован как ссылка (TSysClient) (или это константный указатель ? [*]), и указатель (допустима адресная арифметика), содержащий адрес переменной или объекта.
Да ещё и ссылки...
>> Когда клиент соединяется, в temp_client_2 получаю то что
>> ожидается
>А откуда там взялось это самое "то что ожидается" ? В какой момент >времени, при каком событии ты устанавливаешь значение AThread.Data ?
Athread.Data устанавливаю в OnConnect после заполнения свойств нового клиента (AThread.Data := TObject(SysClient)).
temp_ptr_1 := PTSysClient(AThread.Data);
AThread.Data - указатель на void, типизирую его к PTSysClient (да, получится тип "указатель на указатель", если [*] верно).
temp_client_1 := temp_ptr_1^;
Здесь temp_ptr_1^ возвратит 4 байта - указатель. И это не должны быть первые 4 байта памяти, где физически расположен объект SysClient... И вообще, объекты лежат в куче, указатели - как локальные переменные - в стеке. Т.е. значением temp_client и должны быть крокозяблики... Так и происходит в OnDisconnect и в OnExecute. Но в OnConnect temp_client_1 указывает на клиента корректно (т.е. по тому адресу, куда temp_client_1 указывает, действительно лежит клиент)...
>> Все они глобальные
>Это еще зачем ?
Все эти 4 указателя/объекта я ввёл только для проверки рассматриваемой ситуации...
>Ты про $ что ли ?
>Нет , это не "значение указателя", это вообще непонятно что ..
То, что после этого козяблика, всегда одно и то же... :))
Схема в твоем случае д.б. примерно такая: ...
Всё так и есть, только делал я так:
...PTSysClient(AThread.Data)... // думал, что TSysClient - объект
← →
Сергей М. © (2006-09-07 10:59) [67]
> Получается в делфе 2 типа указателей: указатель на объект,
> который реализован как ссылка (TSysClient) (или это константный
> указатель ? [*]), и указатель (допустима адресная арифметика),
> содержащий адрес переменной или объекта.
В Делфи любая переменная типа класс представлена в памяти как указатель.
Содержимое этой переменной есть адрес фактического местонахождения объекта соответствующего класса.
← →
Cyrax © (2006-09-08 08:44) [68]Интересная вещь получается. Я в первичном потоке два раза запираю clients типа TThreadList (LockList) (эти запоры что - накапливаются ?) и один раз отпираю (там же). Затем во вторичном потоке пытаюcь выполнить LockList и у меня текущая процедура моментально завершается на этом LockList"е, никаких исключений не выбрасывается, в finally даже не попадаю. Т.е. текущая процедура завершается, но прога продолжает работать...
И зачем тогда вообще проверять, заперт ли список или нет, если не могу эту ситуацию отловить...
И ещё. Если эти запоры накапливаются, то UnLock делать придётся для каждого запора. А если запоров много ? - колоссальное загромождение кода try"ами и finally"ами.
А число байт на входе всё-таки придётся узнавать... Потому что в OnExecute у меня происходит сначала отправка данных из выходного буфера соответствующего клиента по сети и приём данних из сети во входной буфер етого же клиента. Таким образом, если я заблокирую поток, например, чтением из сокета, то не смогу отправить данные по сети етого же клиента (до тех пор, пока клиенту не придёт нужное количество байт и поток не разблокируется, а если не придёт ?)...
Так вот, довольно нехорошо получается, что средствами Indy никак не получить число байт на входе, не трогая внутренний буфер...
Dmitrij_K (06.09.06 23:08) [63]
ws2_32.dll
А шо ето разнит winsock2.dll ?
← →
Сергей М. © (2006-09-08 10:09) [69]
> эти запоры что - накапливаются ?
Для каждой создаваемой крит.секции система создает соотв.сч-к блокировок ("запоров" в твоей терминологии).
Логика работы блокировки примерно такова:
при lock сч-к проверяется на равенство 0, и если равен, то сч-к инкрементируется (поток "захватывает" крит.секцию), в противном случае поток блокируется до момента обнаружения обнуления сч-ка.
Каждый unlock, выполненный захватившим секцию потоком, декрементирует сч-к.
> Если эти запоры накапливаются, то UnLock делать придётся
> для каждого запора. А если запоров много ? - колоссальное
> загромождение кода try"ами и finally"ами
Расставлять "запоры" следует с умом, а не везде где попало, типа "на всякий пожарный". Разумеется, избыток "запоров" не приведет к хорошему.
> в OnExecute у меня происходит сначала отправка данных из
> выходного буфера соответствующего клиента по сети и приём
> данних из сети во входной буфер етого же клиента
И это абсолютно нормальная логика взаимодействия сервера с клиентом, "запрос - ответ", "запрос - ответ" ....
Совершенно очевидно, что сторона, пославшая запрос, должна ждать ответа партнера по соединению столь долго, сколь это предусмотрено прикладным протоколом инф.обмена (ППИО), и до этих пор не пытаться отправлять очередные запросы.
Тем же самым ППИО должно быть оговорено, как должны разделяться передаваемые/принимаемые инф.пакеты в сплошном потоке приема/передачи:
1. пакеты предваряются фиксированным (например, 4 байта, содержащие Integer-значение) префиксом размера следующего за ним пакета (если размер пакета не фиксирован протоколом и может варьироваться)
2. пакеты разделяются специальной сигнатурной последовательностью фикс.размера (например, нулевой байт, являющийся символом-терминатором пакета, содержащего строковые данные в ANSI-кодировке)
Если ты в своем ППИО предусмотрел вышеизложенное, то знать размер данных, доступных в очереди приема, совершенно не обязательно, о чем я тебе и говорил.
← →
Cyrax © (2006-09-08 18:00) [70]По поводу ППИО. У меня сервер является промежуточным звеном между клиентами и аппаратурой. Клиенты посылают запросы серверу по TCP/IP, сервер отсылает эти запросы аппаратуре через последовательный порт, затем принимает ответ от аппаратуры и пересылает его нужному клиенту.
ППИО (назовём его П1) организован только между клиентами и аппаратурой, а сервер является лишь промежуточным звеном между ними, т.е. сервер принимает всё, что пришло и пересылает без модификации аппаратуре. И не разбирает П1-пакеты.
Можно, конечно, и "заставить" сервер разбирать П1-пакеты (в моём модуле). Тогда можно будет принимать и отсылать П1-пакеты, например по-одному (и не нужно будет определять число байт на входе). Но в этом случае теряется универсальность модуля из-за ориентации на ППИО П1.
____________________________
> Dmitrij_K (06.09.06 23:08) [63]
> ws2_32.dll
А шо ето разнит winsock2.dll ?
← →
Dmitrij_K (2006-09-08 19:57) [71]
> А шо ето разнит winsock2.dll
Шо? Я не понимать.
← →
Cyrax © (2006-09-08 22:15) [72]Т.е. почему бы не использовать winsock2.dll, с которым работает Indy...
← →
Dmitrij_K (2006-09-08 22:24) [73]библиотеки winsock2.dll не существует
indy работает с WS2_32.DLL
← →
Cyrax © (2006-09-08 23:13) [74]...спутал с делфийским модулем...
← →
Сергей М. © (2006-09-11 08:25) [75]
> сервер принимает всё, что пришло и пересылает без модификации
> аппаратуре. И не разбирает П1-пакеты
Ну и зачем при этом серверу знать размер данных "на входе" ?
Читий равными блоками, например, по 4К. Сколько реально будет прочитано, столько и записывай "на выход".
← →
Cyrax © (2006-09-11 09:37) [76]Сергей М. © (11.09.06 08:25) [75]
Ну и зачем при этом серверу знать размер данных "на входе" ?
Чтобы не блокировать поток, поскольку в OnExecute происходит не только чтение, но и отправка. Попытаемся, например, считать 4Кб, а будет всего 3 - поток заблокируется и мы не сможем отправлять данные, пока не придёт ещё 1 Кб...
Сколько реально будет прочитано
Вот это и нужно знать - сколько их там, байтов...
← →
Сергей М. © (2006-09-11 09:51) [77]
> Cyrax © (11.09.06 09:37) [76]
> Чтобы не блокировать поток, поскольку в OnExecute происходит
> не только чтение, но и отправка
Блокирование происходит не более чем на время, указанное в таймаут-параметре.
> Вот это и нужно знать - сколько их там, байтов
Объект TIdTCPConnection имеет св-во RecvBufferSize - это и есть размер данных, доступных для чтения в дан.момент.
← →
Сергей М. © (2006-09-11 09:59) [78]Вру.
См. св-во Connection.InputBuffer.Size
Кр.того есть метод Connection.CurrentReadBuffer - он вернет строку, длина которой и показывает "сколько их там, байтов"
← →
Cyrax © (2006-09-11 10:15) [79]Connection.InputBuffer.Size
Только для этого задействовать буфер...
Connection.CurrentReadBuffer
Затем преобразовывать строку в цепочку байтов - неэффективно.
Да и внутренний буфер опять задействовать придётся...
← →
Сергей М. © (2006-09-11 10:21) [80]
> Только для этого задействовать буфер..
Что значит "задействовать" ? Он и так задействован в TIdTCPConnection, хочешь тыт того или нет...
> Затем преобразовывать строку в цепочку байтов - неэффективно.
>
> Да и внутренний буфер опять задействовать придётся...
Это еще зачем ?
s := AThread.Connection.CurrentReadBuffer;
if Length(s) > 0 then
AThread.Connection.WriteBuffer(PChar(s)^, Length(s));
Страницы: 1 2 3 4 вся ветка
Форум: "Начинающим";
Текущий архив: 2006.11.12;
Скачать: [xml.tar.bz2];
Память: 0.66 MB
Время: 0.062 c