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

Вниз

Не возвращается количество полученных байт в блокирующисокетах???   Найти похожие ветки 

 
slgeo   (2004-03-19 10:04) [0]

У меня идет обмен по блокирующим сокетам, а точнее передача файла от
серверного потока клиентскому:
на клиенте срабатывает следующий код
   var
    Buf : string;
   
   if Stream.WaitForData(40000) then
     begin
       SetLength(Buf, 18);
       nRead := Stream.Read(Buf[1], 18);
   ...
   end;
   
    Так вот на некоторых машинах, точнее на 2-х из 5-ти, в nRead
    возвращается 0! Но данные на самом деле передаются! На этих 2-х
    установлена Win98 SE, на остальных 3-х машинах, где в nRead
    возвращается количество принятых байт:
    1 - WinXP
    2 - WinXP
    3 - Win98 SE

   Я уже не знаю где искать, замена файлов
   winsock.dll,wsock32.dll,wininet.dll ни к чему не приводит.


 
Verg ©   (2004-03-19 10:10) [1]

nRead =0 в данном случае означает, что соединение было закрыто.


 
Digitman ©   (2004-03-19 10:12) [2]


> У меня идет обмен по блокирующим сокетам


ничего подобного
у тебя обмен идет с использованием НЕблокирующего режима
так что тот результат, что ты получаешь - вполне ожидаем


 
Digitman ©   (2004-03-19 10:15) [3]


> Verg ©   (19.03.04 10:10) [1]


уверен ?


   if FEvent.WaitFor(FTimeOut) <> wrSignaled then //нет данных в буфере приема
     Result := 0
   else
   begin
     GetOverlappedResult(FSocket.SocketHandle, Overlapped, DWORD(Result), False);
     FEvent.ResetEvent;
   end;


 
slgeo   (2004-03-19 10:19) [4]

to Digitman
с чего вдруг???
 OptClientThread = class(TThread)
 private
   ClientSocket: TClientSocket;
 ...
 end;

procedure OptClientThread.Execute;
var
 nRead: Integer;
 Stream: TWinSocketStream;  
 try
   ClientSocket := TClientSocket.Create (nil);
   Stream := nil;
   try
     ClientSocket.Address := ServerAddress;
     ClientSocket.ClientType := ctBlocking;
     ClientSocket.Port := RemotePort;
     ClientSocket.Active := True;
     Stream := TWinSocketStream.Create(ClientSocket.Socket, 30000);
 ...
 except
 ....
 end;


 
Verg ©   (2004-03-19 10:25) [5]

Если  это сработало (вернуло true)

>  if Stream.WaitForData(40000) then


значит в сокете сработал ReadFDS, а это в свою очередь означает, что у сокета во входном потоке что-то появилось. Это могут быть данные, а может быть и признак конца файла ( Read() вернет 0 )


> [2] Digitman ©   (19.03.04 10:12)


А с каким еще сокетом может работать TWinSocketStream, у которого есть метод WaitForData?


 
slgeo   (2004-03-19 10:32) [6]

>Verg

во-во это уже ближе, в Buf попадает что-то типа PK.... (если я не ошибаюсь это действительно признак конца файла). Но ведь с другой стороны был передан файл??? Куда он пропал?

Кстати выше по коду перед приемом файла у меня есть еще прием команды. Так вот в nRead снова 0, но в Buf данные попадают!!!


 
Digitman ©   (2004-03-19 10:36) [7]

при использовании TWinSocketStream.Read запускается асинхронная overlapped-операция ввода

блокирование вызывающего код.потока происходит здесь
 if FEvent.WaitFor(FTimeOut) <> wrSignaled then

а здесь - неблокирующая операция ввода
   if not ReadFile(FSocket.SocketHandle, Buffer, Count, DWORD(Result),
     @Overlapped) and (GetLastError <> ERROR_IO_PENDING) then
   begin
     ErrorCode := GetLastError;
     raise ESocketError.CreateResFmt(@sSocketIOError, [sSocketRead, ErrorCode,
       SysErrorMessage(ErrorCode)]);
   end;


 
Verg ©   (2004-03-19 10:37) [8]

Я там не вижу никакого "приема файла". Я там вижу ожидание появления данных в сокете и попытку проичтать из него не более 18-ти байтов в строку.
Какой файл? Где файл?


 
slgeo   (2004-03-19 10:43) [9]

вот так точнее
var
ms : TMemoryStream;

    if Stream.WaitForData(60000) then
     begin
       Buf := "";
       SetLength(Buf, 256);
       FMaxProgress := fsize;
       Synchronize(DoMaxProgress);
       while True do
       begin
         nRead := Stream.Read(Buf[1],256);
         if Pos("000000!",Buf)>0 then
         begin
           StrIn := Copy(Buf,1,Pos("000000!",Buf)-1);
           ms.Write (strIn[1], Length(strIn));
           FMaxProgress := 0;
           Synchronize(DoMaxProgress);
           Break;
         end
         else
           ms.Write(Buf[1], nRead);
       end;
     end else
         begin
           FLogMsg := "Не получен очередной пакет обновления!!!";
           Synchronize(DoLog);
           Exit;
         end;


 
slgeo   (2004-03-19 10:44) [10]

и дальше естесвенно
     ms.Position := 0;
     ms.SaveToFile(ExtractFilePath(Application.ExeName)"+"update.txt");
     ms.Free;


 
Digitman ©   (2004-03-19 10:52) [11]


> Verg ©   (19.03.04 10:25) [5]


да, я здесь был неточен

гнездо-то само по себе иниц-но для работы в блок.режиме, но работа с ним идет с использованием файлового overlapped-ввода/вывода, что само по себе подразумевает неблок.режим ввода/вывода


> slgeo


вот цитата :

The event specified in the OVERLAPPED structure is set to the signaled state upon completion of the read operation.

это означает, что сигнал ивента следует ожидать только ПОСЛЕ приема и доступности в файловом буфере тех самых запрошенных тобой 18-ти байт

если же ивент не просигналил за 30 секунд (ты сам обозначил это время), то, как видишь из исх.текста, метод Read вернет тебе 0 .. т.е. какая-то часть запрошенных к чтению данных успела прийти за это время, а какая-то еще "в пути"... очевидно, что следует циклически повторять вызов метода Read() до тех пор, пока не просигналит ивент либо не будет возбуждено исключение по потенциально возможному разрыву транспортного канала


 
Verg ©   (2004-03-19 10:55) [12]

2 Digitman


>    if FEvent.WaitFor(FTimeOut) <> wrSignaled then //нет
> данных в буфере приема
>     Result := 0


Вот это есть некая "кривость". Потому как в результате невозможно отличить ситуацию таймаута от ситуации EOF (разрыв соединения собеседником).
Хоть бы -1 присвоили что-ли...

Сокет тем временем именно в блокирующем режиме. А для того, чтобы обеспечить возможность таймаута при чтении там воспользовались Overlapped IO с блокировкаой WaitForSingleObject (хотя можно было бы и select+recv-ом также все сделать).

Т.о. метод Read этого TWinSocketStream блокирующий по сути своей. Т.е. он не вернет управление потоку до тех пор, пока операция не закончится хоть с каким-то результатом.


 
Verg ©   (2004-03-19 10:59) [13]


> успела прийти за это время, а какая-то еще "в пути"... очевидно,
> что следует циклически повторять вызов метода Read() до
> тех пор, пока не просигналит ивент либо не будет возбуждено
> исключение по потенциально возможному разрыву транспортного
> канала


Нет, ты забыл что ли, что waitfordata уже сработал, т.е. в приемном буфере сокета уже что-то должно быть - хоть данные, хоть EOF


 
Digitman ©   (2004-03-19 11:11) [14]


> Verg ©   (19.03.04 10:59) [13]


а waitfordata-то здесь причем ? он не имеет отношения ни к overlapped-операции ввода ни к ивенту ..

ну да, ну вернул он True, что-то там в приехало ..

теперь мы стартуем/рестартуем собственно overlapped-операцию (неблокирующую по определению), после чего тут же анализируем сигнал ивента .. предположим, из запрошенных 18 байт на момент анализа ивента в файловом потоке ввода доступно всего 9 байт ... разумеется, ивент не просигналил еще ! ... а раз не просигналил, то и Result = 0...

ну и иная ситуация (та на которую ты намекаешь) - ивент просигналил, но в файловом потоке есть EOF ... тогда Read() вернет из overlapped-структуры то число байт, что находится в файловом потоке ДО Eof ... и вполне возможно, что до EOF ничего и не приходило ... тогда ситуация с Result = 0 так же объяснима


 
Digitman ©   (2004-03-19 11:18) [15]


> Verg ©   (19.03.04 10:55) [12]


нет, ну насчет того, что Read() сам по себе блокирующий, я согласен на все 100 ...

я имел ввиду саму операцию ввода/вывода, что задействуется внутри Read()

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


 
Verg ©   (2004-03-19 11:26) [16]


> предположим, из запрошенных 18 байт на момент анализа ивента
> в файловом потоке ввода доступно всего 9 байт ... разумеется,
> ивент не просигналил еще !


Ивент просигналит, именно просигналит, если в сокете есть хоть сколько-нибудь принятых байтов, хоть один!
Либо произошел разрыв петли, тогда getoverlappedresult вернет 0.


 
Verg ©   (2004-03-19 11:36) [17]


> [9] slgeo   (19.03.04 10:43)


Не знаю, прямо что и сказать - то один код показываешь, то другой.
Вот в последнем - почему ты так уверен, что признак конца (000000!) не попдет на границе принимаемых блоков?
Где гарантия, что ты его (эту последовательность) обнаружишь целиком в одном эелементарном буфере примема (256 байт)?


 
Verg ©   (2004-03-19 11:44) [18]

Этот код, при определенном размере передаваемого потока может не обнаружить признак конца твоего "файла" и будет продолжать ждать данные от собеседника. А тот, отправив все данные и признак конца 00000! просто закроет соединение.
Вот у тебя и nRead выскочит с нулем.


 
Digitman ©   (2004-03-19 12:03) [19]


> Verg ©   (19.03.04 11:26) [16]


проанализировал ..
согласен, просигналит .. я был не прав


 
slgeo   (2004-03-19 12:43) [20]

>Verg
Ну а почему nRead всегда 0!!!


 
Digitman ©   (2004-03-19 12:54) [21]


> slgeo


приводи код кл.стороны
следы проблемы ведут туда


 
slgeo   (2004-03-19 12:58) [22]

FileSize := FileUtil.GetFileSize(strfile);
     //передаем команду на обновление с размером файла
     strFeedback := "UPDATE!"+ AddChar("0",IntToStr (FileSize),10)+"!";
     ClientSocket.SendText(strFeedback);
     ClientSocket.SendStream(TMyFileStream.Create (
       strFile, fmOpenRead or fmShareDenyWrite));
     ClientSocket.SendText("000000!");


 
slgeo   (2004-03-19 13:01) [23]

function TMyFileStream.Read(var Buffer; Count: Integer): Longint;
begin
result:=inherited Read(Buffer, Count);
Form1.Gauge2.AddProgress(Count);
end;


 
Digitman ©   (2004-03-19 13:05) [24]


> slgeo


почему ты, явно заострив внимание на блок.режиме принимающей стороны, ни словом не обмолвился о режиме передающей ?

это крайне важно !!


 
slgeo   (2004-03-19 13:11) [25]

я думаю это вполне само собой разумеющееся, обе стороны работают в блокирующем режиме


 
Digitman ©   (2004-03-19 13:20) [26]


> я думаю это вполне само собой разумеющееся


с чего бы "разумеющееся" ? режим одной стороны соединения не имеет никакого отношения к режиму другой стороны

вопрос здесь иной : а чем обосновано применение TWinSocketStream
на принимающей стороне и неприменение его же на передающей ? есть разумные доводы в пользу этой "солянки сборной" ?


 
Verg ©   (2004-03-19 13:28) [27]


> [20] slgeo   (19.03.04 12:43)
> >Verg
> Ну а почему nRead всегда 0!!!


Когда "всегда"? Что значит "всегда"?
Не верю. Ты же вот не говоришь - как именно ты убедился, что ВСЕГДА 0.

Или ты чего-то не договариваешь или у тебя "поврежденная" Винда (что крайне маловероятно).

Ты же должен понимать, что "чудес" не бывает.


 
slgeo   (2004-03-19 13:35) [28]

так, начнем сначала:
и приемная и передающая сторона используют TWinSocketStream,
в нашем случае передающая сторона TServerClientThread, а именно сервер обрабатывающий запросы от нескольких клиентов. ПОэтому приведенный выше код [22] возможно ввел немного в заблудение. Этот код находится внутри метода ClientExecute экземпляра TServerClientThread.


 
slgeo   (2004-03-19 13:41) [29]

> Verg
А убедился я поставив в итоге на эту конкретную машину Delphi и увидев под отладчиком значение переменной nRead = 0, причем данные (а именно команда "UPDATE!0013433220!") были прочитаны в буфер!


 
Digitman ©   (2004-03-19 13:42) [30]


> slgeo   (19.03.04 13:35) [28]


приводи ПОЛНЫЙ текст ClientExecute


 
Digitman ©   (2004-03-19 13:48) [31]

с учетом деталей происходящего в [29] есть большое подозрение, что строчка

TMyFileStream.Create (strFile, fmOpenRead or fmShareDenyWrite)

вызвала исключение, в рез-те чего соединение на передающей стороне было закрыто тем или иным образом, что и явилось причиной Eof на принимающей стороне


 
slgeo   (2004-03-19 13:55) [32]

procedure TDbServerThread.ClientExecute;
var
 Stream: TWinSocketStream;
 FileSize : Integer;
 strfile : string;
begin
 Stream := TWinSocketStream.Create(ClientSocket, 30000);
 try
   while not Terminated and ClientSocket.Connected do
   strfile := CurDir+ "update.txt";
   if FileExists (strFile) then
   begin
     FileSize := FileUtil.GetFileSize(strfile);
     strFeedback := "UPDATE!"+ AddChar("0",IntToStr(FileSize),10)+"!";
     ClientSocket.SendText(strFeedback);
     ClientSocket.SendStream(TMyFileStream.Create (
       strFile, fmOpenRead or fmShareDenyWrite));
     ClientSocket.SendText("000000!");
   end;
 finally
   Stream.Free;
 end;
end;

function TMyFileStream.Read(var Buffer; Count: Integer): Longint;
begin
result:=inherited Read(Buffer, Count);
// Form1.Gauge2.AddProgress(Count);
end;


 
Digitman ©   (2004-03-19 13:55) [33]

мне вообще непонятно, на кой шут в дан.случае транслировать что бы то ни было в текстовом формате..

что такое "256" в [9] ? откуда взялось это число ?

твой ПИО предусматривает трансляцию префикса/постфикса ? его длина что, всегда равна 256 байт ?


 
Verg ©   (2004-03-19 14:07) [34]


> [29] slgeo   (19.03.04 13:41)
> > Verg
> А убедился я поставив в итоге на эту конкретную машину Delphi
> и увидев под отладчиком значение переменной nRead = 0, причем
> данные (а именно команда "UPDATE!0013433220!") были прочитаны
> в буфер
!


Или были и до Read?


 
slgeo   (2004-03-19 14:13) [35]

> Verg
нет, это самый первый Read в обработчике


 
Verg ©   (2004-03-19 14:25) [36]


> [35] slgeo   (19.03.04 14:13)


В обработчике чего? Ну и что, что "первый"?

Слушай, ну сколько можно из тебя "клещами"-то все вытаскивать?

Read всегда возвращает истинное положение дел в сокете.
Если он сказал, что ноль байтов принял, значит так оно и есть.
Не веришь?

Я проверял многократно (мягко говоря:) ) - все так и есть.

Если у тебя порежденный драйвер TCP/IP на машине, то это должно сказаться и на других приложениях, работающих через сокеты (через WinSock).


 
slgeo   (2004-03-19 14:39) [37]

вот практически вся процедура принимающей стороны
procedure OptClientThread.Execute;
var
 I, nRead: Integer;
 Stream: TWinSocketStream;
 ms : TMemoryStream;
 Buf, StrIn : String;
 fsize : integer;
 strFile : string;
 FileSize : integer;
begin
 try
   ClientSocket := TClientSocket.Create (nil);
   Stream := nil;
   try
     ClientSocket.Address := ServerAddress;
     ClientSocket.ClientType := ctBlocking;
     ClientSocket.Port := RemotePort;
     ClientSocket.Active := True;
     Stream := TWinSocketStream.Create(ClientSocket.Socket, 30000);
   except
     FLogMsg := "Нет соединения с сервером! Повторите обновление позднее!";
     Synchronize(DoLog);
     Exit;
   end;
     FLogMsg := "Отправляем запрос на обновление...";
     Synchronize(DoLog);
     dmData.IBSQL.Close;
     dmData.IBSQL.SQL.Text := "select sendstatus from settings";
     dmData.IBSQL.ExecQuery;
     ClientSocket.Socket.SendText ("UPD_"+IntToStr(UIN)+"_"+dmData.IBSQL.Fields[0].AsString+"!");
     if Stream.WaitForData(50000) then
     begin
       Buf := "";
       SetLength(Buf, 18);
       nRead := Stream.Read(Buf[1], 18);
//      SetLength(Buf, nRead); если убрать комментарий то Buf очищается        
       if POS("UPDATE!",Buf)=1  then
       begin
          ssize := Copy (Buf, 8, 10);
          try
            fsize := StrToInt(ssize);
          except
            fsize := 0;
          end;
       end
     end else
         begin
           FLogMsg := "Нет ответа на запрос обновления!!!";
           Synchronize(DoLog);
           Exit;
         end;

     //получение пакета обновления
    DeleteFile(ExtractFilePath(Application.ExeName)+"update.txt");
    ms := TMemoryStream.Create;
    FLogMsg := "Загружаем данные...";
    if Stream.WaitForData(60000) then
     begin
       Buf := "";
       SetLength(Buf, 256);
       FMaxProgress := fsize;
       Synchronize(DoMaxProgress);
       while True do
       begin
         nRead := Stream.Read(Buf[1],256);
         if Pos("000000!",Buf)>0 then
         begin
           StrIn := Copy(Buffer,1,Pos("000000!",Buf)-1);
           ms.Write (strIn[1], Length(strIn));
           FMaxProgress := 0;
           Synchronize(DoMaxProgress);
           Break;
         end
         else
         begin
           ms.Write(Buf[1], nRead);
           FProgress := nRead;
           Synchronize(DoProgress);
         end;
       end;
     end else
         begin
           FLogMsg := "Не получен очередной пакет обновления!!!";
           Synchronize(DoLog);
           Exit;
         end;
     FLogMsg := "Загрузка завершена!!!";
     Synchronize(DoLog);
     ms.Position := 0;
     ms.SaveToFile(ExtractFilePath(Application.ExeName)+"inbox\"+"update.zip");
     ms.Free;
    .....
 except
    ....
 end;
end;


 
Verg ©   (2004-03-19 15:28) [38]

Еще раз говорю - значит сервер закрывает соединение (завершает клиентский поток). Возможно он неверно принимает запрос. Ищи там. Пусть сервер ведет лог.

Провеь настройки TCP/IP на "неработающей" машине.

Далее. Проверить надо что именно возвращает

   if FEvent.WaitFor(FTimeOut) <> wrSignaled then
     Result := 0
   else

Если он возвращает не wrSignaled, то тут варанты:
 TWaitResult = (wrSignaled, wrTimeout, wrAbandoned, wrError);

Если этого сделать не сможешь, то просто замени прием на
с nRead := Stream.Read(Buf[1], 18);
на

if Stream.WaitForData(30000) then
 nRead := recv(ClientSocket.SocketHandle, buf[1], length(buf), 0);
else
 nRead := SOCKET_ERROR-1;

Посмотри что скажет.


 
slgeo   (2004-03-19 16:29) [39]

проверил
FEvent.WaitFor(FTimeOut) возвращает wrSignaled !


 
Digitman ©   (2004-03-19 16:35) [40]


> slgeo


слухай сюда ...

при всем моем уважении к тебе, искренне пытающегося найти "первопричину", и к Андрею, искренне пытающегося наставить тебя на путь истинный (имея на о колоссальный практ. им теор. опыт), репу свою чесать по поводу "глюков" должен, в 1-ю очередь, именно ТЫ, и никто иной  ...

и у тепя, мил мой, коль скоро ты пользуешь Делфи, под рукой всегда есть великолепный инструмент под названием Интегрированный Отладчик Делфи ... и какого шута ты им не пользуешься по сей момент - это для нас с Андреем и коллегами составляет великую гостайну ... в каковую (в тайности применения которого в поисках истины) без твоего высочайшего соизволения мы не имеем возможности "въехать"



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

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

Наверх




Память: 0.6 MB
Время: 0.042 c
1-1079963769
Layner
2004-03-22 16:56
2004.04.11
Как после инсталяции сервиса, сразу же запустить его?


6-1079361060
Павел
2004-03-15 17:31
2004.04.11
Сервер удаленного доступа


1-1079697470
Mazer
2004-03-19 14:57
2004.04.11
А как сделать полноцветную иконку в трее (RxTrayIcon)?


6-1079546565
kondryuk
2004-03-17 21:02
2004.04.11
OnGetThread


3-1081503969
GIL
2004-04-09 13:46
2004.04.11
почему не грузится изображение?