Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.11.12;
Скачать: CL | DM;

Вниз

Проблемы при работе с 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;
Скачать: CL | DM;

Наверх




Память: 0.66 MB
Время: 0.055 c
2-1161706340
Max.66RUS
2006-10-24 20:12
2006.11.12
Запись путей ко всем каталогам диска в текстовый файл...


2-1162114969
Серый
2006-10-29 12:42
2006.11.12
Таймер


15-1161504536
Чародей
2006-10-22 12:08
2006.11.12
Поиск похожих jpg


15-1161350780
Petr V.Abramov
2006-10-20 17:26
2006.11.12
Заказчики государственные vs коммерческие


2-1161715996
TStas
2006-10-24 22:53
2006.11.12
Как программно сделать файл ReadOnly?