Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2004.06.20;
Скачать: [xml.tar.bz2];

Вниз

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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.52 MB
Время: 0.037 c
14-1086354506
Drakula
2004-06-04 17:08
2004.06.20
Подскажите программу...


1-1086766518
dimon_programmer
2004-06-09 11:35
2004.06.20
Почему пакет не инсталит .dcu-файл


3-1085639322
lightix
2004-05-27 10:28
2004.06.20
Изменить цвет строки в DBGrid


1-1086726662
lelik
2004-06-09 00:31
2004.06.20
установка программы


1-1086346210
killer
2004-06-04 14:50
2004.06.20
Кнопочка в StringGrid





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский