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

Вниз

TServerSocket/TClientSocket, вычитывание пакетов   Найти похожие ветки 

 
Анонимщик ©   (2004-04-26 17:07) [0]

У меня снова вопрос по TServerSocket/TClientSocket.
Понимаю, что искать чужие ошибки - дело неблагодарное, но у меня впечатление, что я не совсем
понимаю принципа работы функции FServerSocket.OnClientRead.
Ситуация такая. Есть приложение-сервер, в котором создается вторичный поток.
В этом вторичном потоке создается TServerSocket с типом stNonBlocking.
Вторичный поток занимается тем, что вычитывает из аппаратуры некие real-time данные, причем делает это он
где-то раз десять в секунду.
К SocketServer"у коннектятся SocketClient"ы из других приложений, которые запрашивают
данные у этого сервера. После того как запрос на даные получен, сервер ставит флажок,что данному клиенту нужны
данные и, когда данные поступают от аппаратуры, смотрит на флажки всех клиентов, и отсылает данные тем клиентам,
которые их требуют. Непосредственно перед отправкой данных сервер сбрасывает соответствующие флажки.
Клиент, получив и обработав данные, посылает серверу запрос на готовность принять следующую порцию.

Так вот, поработав некоторое время, клиенты уже не получают никакой информации от сервера.
Остановив в это сервер по брейкпоинту, вижу, что значение FServerSocket.Socket.Connections[0].ReceiveLength,
например, равно не нулю, а значению размера того пакета, который должен был бы сообщить серверу, что
клиент готов получить следующий пакет данных. Протоколирование это подтвердило.

Вот примерный код функции. Нужно иметь в виду, что клиент сначала посылает серверу размер данных (как Integer), а затем-
само это количество байт.

 TCurrentData = record
   flgSizeIs: Boolean; // получен ли размер
   fullBufferSize: Integer; // полный размер
   currentBufferSize: Integer; // текущий размер
   currentBuffer: Pointer; // указатель на данные
                end;

 PConnectionData = ^TConnectionData;
 TConnectionData = record
      ...  
      CurrentData: TCurrentData;
                  end;

OnClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
 tmpInt: Integer;
 tmpReceive: Integer;
begin

 ttt := Socket.ReceiveLength;

 if Socket.ReceiveLength > 0 then begin

 pCD := PConnectionData(Socket.Data);

 if pCD = nil // это может быть лишь сообщение от клиента, посылаемое им при коннекте
 then begin
         New(pCD);
         Socket.Data := pCD;

         pCD.CurrentData.currentBuffer := nil;
         pCD.CurrentData.currentBufferSize := -1;
         pCD.CurrentData.fullBufferSize := -1;
         pCD.CurrentData.flgSizeIs := false;

         // получаем размер данных, которые предстоит получить
         Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt));
         pCD.CurrentData.currentBufferSize := 0;
         pCD.CurrentData.fullBufferSize := tmpInt;
         pCD.CurrentData.flgSizeIs := true;
         GetMem(pCD.CurrentData.currentBuffer, tmpInt);

         // далее читаем данные и засовываем их в буфер
         tmpReceive := Socket.ReceiveBuf(Pointer(Integer(pCD.CurrentData.currentBuffer) + pCD.CurrentData.currentBufferSize)^,
                                         pCD.CurrentData.fullBufferSize - pCD.CurrentData.currentBufferSize);
         if tmpReceive >= 0
         then pCD.CurrentData.currentBufferSize := pCD.CurrentData.currentBufferSize + tmpReceive
         else;
      end
 else begin
        if not pCD.CurrentData.flgSizeIs
        then begin // новые данные
               if Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt)) <> SizeOf(tmpInt)
               then begin
                      ShowMessage("int not int");
                      if tmpInt = 0 then
                      begin
                        ShowMessage("buffer size = 0");
                      end;
                    end;
               pCD.CurrentData.currentBufferSize := 0;
               pCD.CurrentData.fullBufferSize := tmpInt;
               pCD.CurrentData.flgSizeIs := true;
               GetMem(pCD.CurrentData.currentBuffer, tmpInt);
             end;
       // флаг получения размера стоит и нужно получать только данные
       tmpReceive := Socket.ReceiveBuf(Pointer(Integer(pCD.CurrentData.currentBuffer) + pCD.CurrentData.currentBufferSize)^,
                                       pCD.CurrentData.fullBufferSize - pCD.CurrentData.currentBufferSize);
       if tmpReceive > ttt then
         ShowMessage("> " + IntToStr(tmpReceive - ttt));
       if tmpReceive >= 0
       then pCD.CurrentData.currentBufferSize := pCD.CurrentData.currentBufferSize + tmpReceive
       else;
      end;

 // теперь смотрим, если данные получены полностью, то интерпретируем их, а потом освобождаем память
 if not(
       (pCD.CurrentData.fullBufferSize = pCD.CurrentData.currentBufferSize) and (pCD.CurrentData.currentBufferSize <> 0)
       and (pCD.CurrentData.currentBufferSize <> -1))
 then exit;

 // интерпретируем полученные данные

...
end;

Все эти ShowMessage никогда ничего на самом деле не показывают. Execute потока имеет вид:

...

 while GetMessage(msg,0,0,0) do
 begin
 DispatchMessage(msg);
 ...
 end;

...

С чем может быть связано такое поведение, а заодно и еще пара вопросов. Как я понимаю, когда для сокет-сервера приходят
данные, операционная система извещает его посылкой сообщения созданному им окну, а далее вызывается вышепреведенная функция.
Иногда при входе в процедуру Socket.ReceiveLength равно нулю. С чем это связано? И куда могут деваться уведомления?
Может, кто-нибудь и в код еще посмотрит?


 
Digitman ©   (2004-04-26 17:23) [1]


>          // получаем размер данных, которые предстоит получить
>          Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt));


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


 
Verg ©   (2004-04-26 17:27) [2]


> Иногда при входе в процедуру Socket.ReceiveLength равно
> нулю. С чем это связано?


Это связано с тем, что обрабатывая OnRead ты иногда читаешь дважды.

OnRead (точнее ставится сообщение FD_READ на очередь) возникает если:
1. Из сети приходит порция данных, а в приемном буфере сокета пусто.
2. Производится чтение из приемного буфера сокета, но после ReceiveBuf в буфере сокета остались данные.

Вот и представь себе, что принятая порция равна, допустим 6 байт.
Возникает OnRead. В обработчике ты читаешь ReceiveBuff(Buffer, 4), т.е. не все данные. Ядром в очередь сообщений ставится еще оди FD_READ, т.к. твое чтение не извлекло всех данных. Но далее ты читаешь еще раз (в этом же обработчике), еще раз вызвав ReceiveBuff(Buffer, 10). Тебе будет возвращено оставшихся 2 байта и приемный буфер сокета оказался пустым. НО! Сообщение-то FD_READ уже поставлено (то самое, в результате первого ReceiveBuff)!
Вот и получаешь потом OnRead при ReceiveLength = 0


 
Анонимщик ©   (2004-04-26 17:37) [3]

Digitman

Я не анализировал только если PCD = nil, это происходит только один раз, при установлении коннекта, и успешно. Т.е. проблема не в этом.


 
Digitman ©   (2004-04-26 17:37) [4]


> Анонимщик


событие On[Client]Read - не более чем факт НЕПУСТОГО буфера приема гнезда

в обработчике ты прочитал ВЕСЬ буфер на ЭТОТ момент ? Событие более не возникнет

НЕ весь ? скажем, частично ? событие возникнет вновь !


 
Digitman ©   (2004-04-26 17:42) [5]


> Анонимщик


в большинстве алгоритмических случаев анализ ReceiveLength - бестолковое занятие и пустая трата времени, ибо (цитата из справки)

Note: ReceiveLength is not guaranteed to be accurate for streaming socket connections.

гораздо более "серьезное" занятие - вызов recv-функций гнезда и анализ результата вызова этих ф-ций


 
Анонимщик ©   (2004-04-26 17:51) [6]

а разве уменя "streaming socket connections"?

Но все-таки, мне кажется, проблема в чеи-мто другом.


 
Анонимщик ©   (2004-04-26 17:57) [7]

Verg

Тебе спасибо тоже большое


 
Digitman ©   (2004-04-26 17:58) [8]


> а разве уменя "streaming socket connections"?


именно оные ! ибо компоненты, тобой используемые, задействуют поточно ориентируемый TCP-протокол


> мне кажется, проблема в чеи-мто другом


может быть

вникать в весь код, когда невоор.глазом видны явные несуразности, желания  - поверь уж - нет никакого

исправляй свой код, принимай во внимание [1] - тогда и будет детальный разговор


 
Анонимщик ©   (2004-04-26 19:11) [9]

Я с чего-то решил, что этот самый поточный коннект имеется в виду только при использовании TWinSocketStream. Ну да ладно.

Там, где Socket.ReceiveBuf не проверяется, работает, хотя и исправлю, конечно.

Но смотри.
Поставил еще один вызов в том месте, где проверяются флажки вида:
if (FServerSocket.Socket.Connections[i].ReceiveLength > 0)
then OnClientReadOfServerSocket(nil, FServerSocket.Socket.Connections[i]);

Когда брейкпоинт сработал, дошел в вышеприведенном коде до строки:

if not pCD.CurrentData.flgSizeIs
then begin // новые данные
if Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt)) <> SizeOf(tmpInt)
then begin ...

Так вот, до того, как выполнились операторы
Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt)) <> SizeOf(tmpInt)
значение
Socket.ReceiveLength было равно восьми, а после выполнения - нулю. Как это может такое быть?


 
Polevi ©   (2004-04-27 08:17) [10]

ReceiveLength показывает колво байт в буфере винсок, ReceiveBuf выбирает данные из буфера винсок
было 8 байт , 8 прочитали - сколько осталось байт ?


 
Digitman ©   (2004-04-27 08:47) [11]


> Анонимщик


вот тебе дался этот ReceiveLength !

методом ReceiveBuf ты считывай ровно столько байт, сколько тебе осталось до заполнения структуры tmpInt ..

пусть, к примеру, SizeOf(tmpInt) = 8

возникло самое первое OnRead-событие, ты вызвал BytesRead := ReceiveBuf(.., 8)

пусть после этого BytesRead = 3

о чем это говорит ? о том, что получил ты на сей момент лишь 3 первых байта ожидаемой структуры ..

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

BytesRead := ReceiveBuf(адрес_структуры + 3, 5);

если вновь BytesRead < 5 , скажем, BytesRead = 3, то вновь инкрементируешь смещение в буфере структуры, из сч-ка оставшихся байт вычитаешь 3 и вновь ждешь события OnRead

и так до тех пор пока не получишь все 8 байт структуры


 
Анонимщик ©   (2004-04-27 10:26) [12]

Все верно, да только tmpInt имеет тип Integer, т.е. SizeOf(tmpInt) равно 4. Теперь:
//Socket.ReceiveLength = 8
if Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt)) <> SizeOf(tmpInt)
//а здесь уже Socket.ReceiveLength равно 0, хотя не может быть меньше 4....


 
Digitman ©   (2004-04-27 10:49) [13]

var
 tmpInt: Integer;
 BytesNeed: Integer = SizeOf(tmpInt);
...
 
procedure XXX.OnRead(..);
var
 ptmpInt: PByte;
 BytesRead: Integer;
begin
 ptmpInt := PByte(Integer(@tmpInt) + SizeOf(tmpInt) - BytesNeed);
 while BytesNeed > 0 do
  begin
    BytesRead := Socket.ReceiveBuf(ptmpInt^, BytesNeed);
    if BytesRead > 0 then
      begin
       Dec(BytesNeed, BytesRead);
       Inc(ptmpInt, BytesRead);
      end
    else
      Break;
  end;  
end;


 
Анонимщик ©   (2004-04-27 13:32) [14]

Digitman,
у меня вопрос уже трансформировался и состоит в том , что
"
Все верно, да только tmpInt имеет тип Integer, т.е. SizeOf(tmpInt) равно 4. Теперь:
//Socket.ReceiveLength = 8
if Socket.ReceiveBuf(tmpInt, SizeOf(tmpInt)) <> SizeOf(tmpInt)
//а здесь уже Socket.ReceiveLength равно 0, хотя не может быть меньше 4
...
"

Я не упоминал, но для междупоточной синхронизации использовал критические секции. Полагаю, дело как раз в этом. Сейчас я не обрабатываю данные по уведомлению, а в явном виде проверяю, есть ли данные
в буфере (та же злополучная ReceiveLength) и руками затем обрабатываю вход. И последние пять минут работа устойчивая, а раньше не работало и 30-ти секунд.

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


 
Digitman ©   (2004-04-27 13:57) [15]


> для междупоточной синхронизации


да какая, к дьяволу, "междупоточная синхронизация" ?

я смотрю тему вопроса, в ней есть явное упоминание события OnClientRead, которое возбуждается ТОЛЬКО если режим работы сервера stNonBlocking ... какие, к дьяволу, потоки ?! как это увязывается с возбуждением компонентом, работающем в stNonBlocking событий доступности данных для чтения ?!


 
Анонимщик ©   (2004-04-30 19:53) [16]

Возвращаюсь снова.
Ситуация такая, алгоритм разбора исправил и все заработало. Но только если адрес и сервера и клиента - 127.0.0.1. Как только переставляю адрес на мой сетевой (192.168.42.14), все, тут же время от времени случается следующее: некоторые пакеты вида
(размер пакета : integer, данные длиной размера пакета) от сервера к клиенту не доходят. Причем отдельные куски не теряются.
Что может быть причиной?


 
Анонимщик ©   (2004-04-30 20:01) [17]

Вопрс снят окончательно. Спасибо.



Страницы: 1 вся ветка

Текущий архив: 2004.06.20;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.023 c
14-1086268979
WebErr
2004-06-03 17:22
2004.06.20
Сканер штрих кода и его сообщения


14-1086082718
Computerny_Geniy
2004-06-01 13:38
2004.06.20
Сообщение перед спящим режимом


9-1077653558
Support1
2004-02-24 23:12
2004.06.20
Вопрос по DelphiGFX .....


4-1084558183
Druid
2004-05-14 22:09
2004.06.20
Загрузка строки из памяти в TMemo


10-1012977852
Matushkin
2002-02-06 09:44
2004.06.20
CORBA сразу и клиент и сервер возможен?