Форум: "Сети";
Текущий архив: 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