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

Вниз

Вопрос про сокеты, а точнее про FD_CLOSE и FD_READ   Найти похожие ветки 

 
__Unnamed__   (2007-10-01 17:52) [0]

Сделал сервак на основе сокетов. Использовал WS Api функции. Обработка информации происходит следуюющим образом:

Да это все в потоке :)

While not Terminated do
 Begin
    Res:=WaitForMultiPleObjects(1,FSockEvent,False,100,Flase);
    Case Res Of
      0: Begin
             ...
              <здесь проверяю флаги>
              FD_CLOSE
              FD_READ
              FD_WRITE
             ...
          End;
    End;
 End;
Суть вопроса! :)
Выявил следующую ошибку.
Когда передаю большой файл >1Mb по нескольким соединениям(т.е. различные файлы, но соединений более 1-го) иногда не доходит до получателя последний блок. Экспериментальным путем выявил, что  FD_Close приходит раньше, чем FD_READ.
Т.к. сервер не мой (т.е. не мною писанный). логика сервера заключается в том, что при посылке конечного куска файла он завершает соединение Shutdown + CloseSocket Или просто CloseSocket (Онное мне не известно).
Как я помню из сокетов на основе оконных функций FD_CLOSE приходит всегда после того как прийдет последний байт из буфера чтения.
Еще прочитал про критические секции, возможно в этом решение. Т.к. при изучении модуля ScktComp обнаружил, что  любая операция I/O заключена в Enter и Leave.
Скажите не потеряется ли быстродействие и независимость самого потока при использовании критических секций.
Или же моя ошибка заключается в другом? :)


 
__Unnamed__   (2007-10-01 18:16) [1]

Да когда делал, через оконные функции, то все доходило нормально, но как-то хотелось бы, чтобы все было в потоке :)
Заранее спасибо!


 
__Unnamed__   (2007-10-01 18:44) [2]

Может конечно вопрос звучит глупо, но после FD_CLOSE приходит FD_READ в котором доходит оставшийся кусок!!!!
Но после FD_CLOSE как положенно можно выходить из цикла и потока, поэтому я и написал сюда, может быть кто-нибудь подскажет.
Может нужно использовать критические секции?
Режим у сокетов неблокирующий.
Просто из описания критических секций я понял, что остальные потоки приостанавливаются для ожидания выполнения самой критической секции.
Т.о. независимость каждого потока нарушается, как я понимаю. Все ждут одного????!!!!!!
Если я не прав пожалуйста поправте :)
И почему Borland в ScktComp везде на операцияx чтения/записи использует CriticalSection?


 
__Unnamed__   (2007-10-01 21:59) [3]

Может кто-нибудь подскажет, что это может быть?
Или все будут морозиться...


 
__Unnamed__   (2007-10-01 22:13) [4]

Ответьте на вопрос.
Может ли прийти FD_CLOSE, если буфер приема еще не пуст?


 
Slym ©   (2007-10-02 04:32) [5]

FD_CLOSE и FD_READ могут приходить одновременно...
WSAEnumNetworkEvents выдает все текущие флаги и проверять надо так
if (lNetworkEvents and FD_READ)<>0 then
begin
//reading
end;
if (lNetworkEvents and FD_CLOSE)<>0 then
begin
//Closing
end;

http://www.sources.ru/cpp/cpp_network_evets_winsock2.shtml


 
Сергей М. ©   (2007-10-02 09:06) [6]


> Может ли прийти FD_CLOSE, если буфер приема еще не пуст?


Может.


> почему Borland в ScktComp везде на операцияx чтения/записи
> использует CriticalSection?


Видимо, просто перестраховался, поскольку в Winsock send/recv-вызовы сами по себе потокобезопасны (по кр.мере на NT-платформе).

С другой стороны, нужно было как-то защитить данные TCustomWinSocket-объектов от потенциальной одновременой их модификации со стороны более чем одного потока при обращении к Send/Receive-методам этих объектов.


 
__Unnamed__   (2007-10-02 21:54) [7]

>>Сергей М. ©

А что можеш предложить, когда у меня иногда не досылает до 100К и более.

Кострукция типа:

If FD_READ Then
 ...
Else
  If FD_CLOSE Then
    ...
Очень спасает, но я не получаю тогда извещения о дисконнекте.

Вот лог:

GET http://localhost/ HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)
Host: localhost
Proxy-Connection: Keep-Alive

HTTP/1.1 200 OK
Date: Tue, 02 Oct 2007 11:33:44 GMT
Server: Apache/1.3.33 (Win32) PHP/4.4.4
Last-Modified: Sun, 27 Aug 2006 13:03:10 GMT
ETag: "0-36fb-44f1980e"
Accept-Ranges: bytes
Content-Length: 14075
Connection: close
Content-Type: text/html
X-Pad: avoid browser bug

Это лог соединений:

Server(236) connected!
Server(236) write: 331
Server(236) read: 7300
Server(236) read: 1175
Server(236) disconnect
//------------------------------
Это после FD_CLOSE еще два цикла
Server(236) read: 4380
Server(236) read: 1503

Полученных данных 7300+1175=8475, а должно быть 14075+283(размер заголовка ответа)=14358.


 
__Unnamed__   (2007-10-02 22:00) [8]

Два цикла - в смысле два такта входа в цикл с
WaitForMultipleObjects


 
__Unnamed__   (2007-10-02 22:12) [9]

>>Slym ©

Function GetEvent(NetEvent:TWSANetworkEvents;Flag:Integer;Var Error:Integer):Boolean;
Begin
 Result:=NetEvent.lNetworkEvents And Flag<>0;
 If Result Then
   Begin
     Case Flag of
       FD_READ:Error:=NetEvent.iErrorCode[FD_READ_BIT];
       FD_WRITE:Error:=NetEvent.iErrorCode[FD_WRITE_BIT];
       FD_CLOSE:Error:=NetEvent.iErrorCode[FD_CLOSE_BIT];
       FD_CONNECT:Error:=NetEvent.iErrorCode[FD_CONNECT_BIT];
       FD_ACCEPT:Error:=NetEvent.iErrorCode[FD_ACCEPT_BIT];
       FD_OOB:Error:=NetEvent.iErrorCode[FD_OOB_BIT];
     End;
   End;
End;

Это обработка События:
WAIT_OBJECT_0: //Server Socket Event
         Begin
           FError:=WSAEnumNetworkEvents(FServerS,FEvent,@NetEvent);
           If FError<>0 Then
             Begin
               FError:=WSAGetLastError;
               MakeError(True);
               Break;
             End;
           If GetEvent(NetEvent,FD_CONNECT,FError) Then
             Begin
               If FError<>0 Then
                 Begin
                   MakeError(True);
                   DisconnectServer;
                   Log.Add("Server("+IntToStr(FServerS)+") connect error: "+IntToStr(FError));
                 End
               Else
                 Begin
                   FServerStatus:=ST_CONNECTED;
                   Log.Add("Server("+IntToStr(FServerS)+") connected!");
                 End;
             End;
           //FD_READ Chtenie dannix ot Apache
           If GetEvent(NetEvent,FD_READ,FError) Then
             Begin
               If FError<>0 Then Break;
               L:=SockRead(FServerS,ClientMem);
               If L=Socket_Error Then
                 Begin
                   MakeError(True);
                   Log.Add("Server("+IntToStr(FServerS)+") read error: "+IntToStr(FError));
                   Break;
                 End;
               Inc(FCountReceive,L);
               Log.Add("Server("+IntToStr(FServerS)+") read: "+IntToStr(L));
             End
           
           //FD_CLOSE Zakritie soedinenie s Apache
           If GetEvent(NetEvent,FD_CLOSE,FError) Then
             Begin
               If FError<>0 Then
                 MakeError(True);
               DisconnectServer;
               Log.Add("Server("+IntToStr(FServerS)+") disconnect");
             End;

           //FD_WRITE Zapis dannix k Apache
           If GetEvent(NetEvent,FD_WRITE,FError) Then
             Begin
               If FError<>0 Then Break;
               L:=SockWrite(FServerS,ServerMem);
               If L=Socket_Error Then
                 Begin
                   MakeError(True);
                   Log.Add("Server("+IntToStr(FServerS)+") write error: "+IntToStr(FError));
                   Break;
                 End;
               Log.Add("Server("+IntToStr(FServerS)+") write: "+IntToStr(L));
             End;
           //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
         End;


 
__Unnamed__   (2007-10-02 22:23) [10]

>>Slym ©
Я хотел показать, что я уже перепробовал все, что возможно и порядок обработки сообщений я знаю, но вот как можно правильно обработать ситуацию описанную выше, я просто теряюсь. :)

Поэтому просто КРИЧУУУУУУУ о помощи.......... :)


 
__Unnamed__   (2007-10-02 22:48) [11]

>>Slym ©
Да извиняюсь, я сверху правда не так написал как в исходнике, у меня вчера при себе кода не было :)


 
Slym ©   (2007-10-03 05:01) [12]

а в чем кстати принципиальность использования неблокирующего режима? Всеравно в потоке и с блокировкой (WaitFor)... и без использования готовых компонент.
И как ты обрабатываешь FD_WRITE/FD_READ - ведь операция записи/чтения может оборваться WSAEWOULDBLOCK и при следующем FD_WRITE/FD_READ  нужно дописывать/дочитывать оставшиеся данные а не все. может в этом затык?


 
__Unnamed__   (2007-10-03 17:08) [13]

Function  SockRead;
Var
 TempBuff:TBuffType;
 CountR  :Integer;
Begin
 Result:=0;
   Result:=IOCTLSocket(Sock,FIONREAD,Cardinal(CountR));
 If Result=Socket_Error Then
   Begin
     FError:=WSAGetLastError;
     Exit;
   End;
 Try
   SetLength(TempBuff,CountR);
   Result:=Recv(Sock,TempBuff[0],CountR,0);
   If Result=SOCKET_ERROR Then
     Begin
       FError:=WSAGetLastError;
       SetLength(TempBuff,0);
       Exit;
     End;
   Buff.Position:=Buff.Size;
   Buff.Write(TempBuff[0],Result);
 Finally
   SetLength(TempBuff,0);
 End;
End;

Function  SockWrite;
Var
 CountW:Integer;
 CountS:Integer;
Begin
 Result:=0;
 IF Buff.Size<=0 Then Exit;
 CountW:=Buff.Size;
 While CountW>0 do
   Begin
     CountS:=CountW;
     If CountS>MaxSend Then CountS:=MaxSend;
     CountS:=Send(Sock,Buff.MemoryAddr^,CountS,0);
     If CountS=Socket_Error Then
       Begin
         FError:=WSAGetLastError;
         If FError=WSAEWOULDBLOCK Then FError:=0
           Else Result:=SOCKET_ERROR;
         Exit;
       End;
     Inc(Result,CountS);
     Buff.Position:=0;
     Buff.Delete(CountS);
     Dec(CountW,CountS);
   End;
End;


 
DVM ©   (2007-10-03 17:16) [14]


> __Unnamed__  

Если все равно создал доп поток то не проще ли использовать блокирующие сокеты, а не асинхронные сокеты на событиях?


 
Сергей М. ©   (2007-10-03 17:16) [15]


> что можеш предложить


А какое отношение к этому имеют CriticalSections ?

И где у тебя реакция на WSAGetLastError = WSAEWOULDBLOCK ?


 
__Unnamed__   (2007-10-03 17:37) [16]

Извиняюсь но в чтении разве может быть WSAEWOULDBLOCK?
И если оно есть, то что оно означает?


 
DVM ©   (2007-10-03 17:43) [17]


> Извиняюсь но в чтении разве может быть WSAEWOULDBLOCK?

Может. Ты хотя бы справку то по recv() открыл бы.


 
__Unnamed__   (2007-10-03 17:53) [18]

Ща открываю :)

Вот цитирую:

If no incoming data is available at the socket, the recv call waits for data to arrive unless the socket is nonblocking. In this case, a value of SOCKET_ERROR is returned with the error code set to WSAEWOULDBLOCK. The select, WSAAsyncSelect, or WSAEventSelect calls can be used to determine when more data arrives.

А теперь смотрим топик 13

Result:=IOCTLSocket(Sock,FIONREAD,Cardinal(CountR));
If Result=Socket_Error Then
  Begin
    FError:=WSAGetLastError;
    Exit;
  End;


В этом куске кода я проверяю кол-во пришедших байт и не пытаюсь закачать больше!


 
__Unnamed__   (2007-10-03 18:04) [19]

Единственное я не проверял  CountR>8192 (Max Receive)
Возможно там больше, но как показывает практика еще ниразу не было такого, что описанная в 18 топике конструкция выдавала бы блоьше 8Кб :\


 
__Unnamed__   (2007-10-03 18:16) [20]

Я извиняюсь за назойливость, коментарии по 18 и 19 будут? :)


 
Сергей М. ©   (2007-10-04 08:33) [21]


> как показывает практика еще ниразу не было такого, что описанная
> в 18 топике конструкция выдавала бы блоьше 8Кб


Все правильно.
Этот дифолт определен опциями SO_SNDBUF и SO_RCVBUF на сторонах передатчика и приемника соответственно.

см. Get/SetSockOpt


 
__Unnamed__   (2007-10-04 12:14) [22]

>>Сергей М.

Я просто уже не знаю что делать, как мне кажеться, WSAEWOULDBLOCK у меня в чтении произойти не может. И в тоже время приходит раньше FD_CLOSE чем опустеет буфер приемника :/.

>>DVM
Просто поймите меня правильно, логика основанная на Асинх/Синхр сокетах должна одинаково получать данные от передающего. Если только вы не намекаете на RECV(...)=0 ==FD_CLOSE, но тут возникает вопрос, почему приходит FD_CLOSE раньше, чем освободится буфер.

Я уже наверное всех достал :) Ну не могу я понять этого...!!!!??????


 
Сергей М. ©   (2007-10-04 12:28) [23]


> как мне кажеться, WSAEWOULDBLOCK у меня в чтении произойти
> не может.


Ну да, не может, ведь ты вызываешь ф-цию recv() как реакцию на событие FD_READ.
Но тем не менее, если бы эта "ошибка" фигурировала, в лог ты ее записал бы, хотя ошибкой такая ситуация на самом деле не является.


> почему приходит FD_CLOSE раньше, чем освободится буфер


> сервер не мой


Тогда откуда ты знаешь, что сервер вызывает shutdown ?
И тем более откуда ты знаешь, с какаим how-параметром сервер вызывает эту функцию ?

Кстати, рекомендацией Slym ©   (02.10.07 04:32) [5] ты воспользовался ?


 
Сергей М. ©   (2007-10-04 12:38) [24]

Цитата из справки:

If how is SD_SEND, subsequent sends are disallowed. For TCP sockets, a FIN will be sent

Из этого следует, что если партнер по соединению запустил асинхронный send, после чего немедленно выполнил shutdown(SD_SEND), вместо ожидаемого тобой SYN ты запросто можешь получить FIN, что как раз и означает потерю того финального шматка потока, передаваемого тебе партнером.


 
Сергей М. ©   (2007-10-04 12:40) [25]


> асинхронный send


Не только асинхронный - и синхронный тоже.


 
__Unnamed__   (2007-10-04 12:42) [26]

1. Сервер Apache но локально(хотя это не имеет значения)
2. Рекомендации Slym-а смотрел см [9], я так и делал просто когда писал вопрос, первое что в голову пришло это было FD_CLOSE :)

Как можно проверить с каким пар-ром ShutDown был выполнен с серверной стороны? Я про это еще нигде не видел примеров и статей :)
Я со своей стороны выполняю всегда ShutDown(FServerS, ??_BOTH);


 
Сергей М. ©   (2007-10-04 12:42) [27]

Короче, сервер вполне м.б. "кривым".

Ну а твоя "кривизна" как клиента исправляется задействованием WSAEnumNetWorkEvents


 
__Unnamed__   (2007-10-04 12:42) [28]

Ну и CloseSocket после ShutDown соотвественно :)


 
Сергей М. ©   (2007-10-04 12:44) [29]


> Как можно проверить с каким пар-ром ShutDown был выполнен
> с серверной стороны?


Кроме анализа текста сервера - никак.
Кстати, Апач - опенсурсный продукт.


> Я со своей стороны выполняю всегда ShutDown(FServerS, ??
> _BOTH);


Нафига ?


 
Сергей М. ©   (2007-10-04 12:46) [30]


> я так и делал


Ну так первым дело м при этом ты должен был искать в списке событий, возвращенных вызовом этой ф-ции, событие FD_READ и только потом FD_CLOSE, но не наоборот.


 
__Unnamed__   (2007-10-04 12:47) [31]

Я столкнулся с такой проблемой, когда я не выполняю ShutDown сам Апаче сервер не закрывает со своей стороны Гнезда и ждет непонятно чего??!!!!!
А с ShutDown все нормально :)


 
Сергей М. ©   (2007-10-04 12:48) [32]


> когда я не выполняю ShutDown сам Апаче сервер не закрывает
> со своей стороны Гнезда и ждет непонятно чего


Быть того не может.

Если ты выполнил без ошибок closesocket, то рано или поздно партнер получит FIN.


 
__Unnamed__   (2007-10-04 12:49) [33]

>>Сергей М. ©

Ну так первым дело м при этом ты должен был искать в списке событий, возвращенных вызовом этой ф-ции, событие FD_READ и только потом FD_CLOSE, но не наоборот.


См. топик [9]!!!


 
Сергей М. ©   (2007-10-04 12:51) [34]

Весьма полезным будет также изучить поведение гнезда при его закрытии при взведенной и сброшенной опции SO_LINGER


 
__Unnamed__   (2007-10-04 13:01) [35]

Т.е. при SO_LINGER = TRUE с моей стороны, все данные будут приняты?
Или это только для посылки?


 
Сергей М. ©   (2007-10-04 13:12) [36]

Это, в свою очередь, зависит от Interval.


 
Сергей М. ©   (2007-10-04 13:16) [37]

Подозреваю, что где-то что-то ты недопонимаешь.

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

Пробуй ту же логику в блокирующем режиме. Если все будет ок, то неправ ты, иначе Апач.


 
__Unnamed__   (2007-10-04 20:28) [38]

Скажи, а если у меня происходит скажем задержка потока на 1-5 секунды в связи с записью на устройство. И чтение из буфера приемника задерживается, то по логике TCP FIN прийдет только тогда, когда Апач полностью передаст данные, ну или у него SO_LINGER=False и прийдет дисконнект но в буфере приемника может быть где-то до 8-64Кб информации как я понимаю.

И мне кажеться в моем случае, мне прийдется анализировать
<Content-length:?????> + FD_CLOSE и его ошибки. Если при дисконнекте произошли ошибки, то выходить иначе читать до тех пор пока не будет равно ожидаемого.


 
Сергей М. ©   (2007-10-05 08:58) [39]

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


> мне прийдется анализировать
> <Content-length:?????>


Да, придется.


 
имя   (2008-02-05 19:35) [40]

Удалено модератором



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

Форум: "Сети";
Текущий архив: 2011.04.24;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.005 c
15-1294435799
Юрий
2011-01-08 00:29
2011.04.24
С днем рождения ! 8 января 2011 суббота


15-1294694994
Юрий
2011-01-11 00:29
2011.04.24
С днем рождения ! 11 января 2011 вторник


2-1295539697
Ain
2011-01-20 19:08
2011.04.24
Edit


15-1294954192
Юрий
2011-01-14 00:29
2011.04.24
С днем рождения ! 14 января 2011 пятница


15-1294834077
George
2011-01-12 15:07
2011.04.24
TIdHTTP.Get и Windows 7





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