Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 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.054 c
1-1159639343
Calibr
2006-09-30 22:02
2006.11.12
вопрос по Variant?


2-1162152048
md
2006-10-29 23:00
2006.11.12
ошибочка вышла


2-1161758334
yel
2006-10-25 10:38
2006.11.12
GLScene хочет Python_d7 и dws2Runtime


2-1162130694
tmpfolder
2006-10-29 17:04
2006.11.12
Record + PString


9-1138627901
Randomaise
2006-01-30 16:31
2006.11.12
Народ подскажите





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский