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

Вниз

Вопрос про сокеты, а точнее про 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]

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


 
ага   (2008-02-06 13:22) [41]

Просмотрел бегло, может уже сказано, но не заметил.

Нормально это, FIN пришел вместе с последним пакетом. Сервер закончил запись и вызвал shutdown на свой передающий конец - типа больше ниче передавать не буду. Сокет отправил все из буфера и с последним пакетом - FIN. Получив его, нужно вычерпать все из буфера и закрыть свой приемный конец.


 
имя   (2009-02-04 17:32) [42]

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


 
FireMan_Alexey   (2009-02-04 18:29) [43]

Сделай так:

While True do
Begin
 Err:=Recv(...);
 IF err=SOCKET_ERROR and WSAGetLastError=WSAEWOULDBLOCK Then
   Break;
End;
Event - сигнализирует о приходе данных, а IOCTLSocket - покажет всего 8192 макс хотя там может быть и больше :) (проверенно экспериментально)
А вообще лучше использовать Select в потоках :) тоже экспериментально :)
Я тоже пытался прокси на Эвентах написать, но столкнулся с такой же ситуацией и с не закрытыми соединениями со стороны апачей :).
Спасло только то что весь код переделал под синхронные сокеты.


 
!"№;%:?*   (2009-03-12 07:30) [44]

При известном количестве данных - читай сколько указано в заголовке, recv вернет ошибку если соединение будет закныто раньше.

В отдельном потоке, можно и блокирующими сокетами обойтись.


 
Palladin ©   (2009-03-12 08:28) [45]


> FireMan_Alexey   (04.02.09 18:29) [43]

А ничего что год прошел? :)



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

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

Наверх




Память: 0.61 MB
Время: 0.011 c
15-1294807250
Xmen
2011-01-12 07:40
2011.04.24
переход в DevExpress


15-1294837507
AlexDn
2011-01-12 16:05
2011.04.24
Гугл кеширование?


2-1295382612
Студент
2011-01-18 23:30
2011.04.24
Насколько я неправильно вывожу на печать через TPrinter?


15-1294596688
@!!ex
2011-01-09 21:11
2011.04.24
PropSheet не работает под Win 7 x64


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