![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Вопрос про сокеты, а точнее про FD_CLOSE и FD_READ [D6, Win95/98, WinME, Win2k, WinXP]
__Unnamed__ © (2007-10-01 17:52:00)Сделал сервак на основе сокетов. Использовал 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.
Скажите не потеряется ли быстродействие и независимость самого потока при использовании критических секций.
Или же моя ошибка заключается в другом? :)
Сергей М. (2007-10-04 12:40:00) [21]
> асинхронный send
Не только асинхронный - и синхронный тоже.
Сергей М. (2007-10-04 12:38:00) [22]Цитата из справки:
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:28:00) [23]
> как мне кажеться, WSAEWOULDBLOCK у меня в чтении произойти
> не может.
Ну да, не может, ведь ты вызываешь ф-цию recv() как реакцию на событие FD_READ.
Но тем не менее, если бы эта "ошибка" фигурировала, в лог ты ее записал бы, хотя ошибкой такая ситуация на самом деле не является.
> почему приходит FD_CLOSE раньше, чем освободится буфер
> сервер не мой
Тогда откуда ты знаешь, что сервер вызывает shutdown ?
И тем более откуда ты знаешь, с какаим how-параметром сервер вызывает эту функцию ?
Кстати, рекомендацией Slym © (02.10.07 04:32) [5] ты воспользовался ?
__Unnamed__ (2007-10-04 12:14:00) [24]>>Сергей М.
Я просто уже не знаю что делать, как мне кажеться, WSAEWOULDBLOCK у меня в чтении произойти не может. И в тоже время приходит раньше FD_CLOSE чем опустеет буфер приемника :/.
>>DVM
Просто поймите меня правильно, логика основанная на Асинх/Синхр сокетах должна одинаково получать данные от передающего. Если только вы не намекаете на RECV(...)=0 ==FD_CLOSE, но тут возникает вопрос, почему приходит FD_CLOSE раньше, чем освободится буфер.
Я уже наверное всех достал :) Ну не могу я понять этого...!!!!??????
Сергей М. (2007-10-04 08:33:00) [25]
> как показывает практика еще ниразу не было такого, что описанная
> в 18 топике конструкция выдавала бы блоьше 8Кб
Все правильно.
Этот дифолт определен опциями SO_SNDBUF и SO_RCVBUF на сторонах передатчика и приемника соответственно.
см. Get/SetSockOpt
__Unnamed__ (2007-10-03 18:16:00) [26]Я извиняюсь за назойливость, коментарии по 18 и 19 будут? :)
__Unnamed__ (2007-10-03 18:04:00) [27]Единственное я не проверял CountR>8192 (Max Receive)
Возможно там больше, но как показывает практика еще ниразу не было такого, что описанная в 18 топике конструкция выдавала бы блоьше 8Кб :\
__Unnamed__ (2007-10-03 17:53:00) [28]Ща открываю :)
Вот цитирую:
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;
В этом куске кода я проверяю кол-во пришедших байт и не пытаюсь закачать больше!
DVM (2007-10-03 17:43:00) [29]
> Извиняюсь но в чтении разве может быть WSAEWOULDBLOCK?
Может. Ты хотя бы справку то по recv() открыл бы.
__Unnamed__ (2007-10-03 17:37:00) [30]Извиняюсь но в чтении разве может быть WSAEWOULDBLOCK?
И если оно есть, то что оно означает?
Сергей М. (2007-10-03 17:16:00) [31]
> что можеш предложить
А какое отношение к этому имеют CriticalSections ?
И где у тебя реакция на WSAGetLastError = WSAEWOULDBLOCK ?
DVM (2007-10-03 17:16:00) [32]
> __Unnamed__
Если все равно создал доп поток то не проще ли использовать блокирующие сокеты, а не асинхронные сокеты на событиях?
__Unnamed__ (2007-10-03 17:08:00) [33]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;
Slym (2007-10-03 05:01:00) [34]а в чем кстати принципиальность использования неблокирующего режима? Всеравно в потоке и с блокировкой (WaitFor)... и без использования готовых компонент.
И как ты обрабатываешь FD_WRITE/FD_READ - ведь операция записи/чтения может оборваться WSAEWOULDBLOCK и при следующем FD_WRITE/FD_READ нужно дописывать/дочитывать оставшиеся данные а не все. может в этом затык?
__Unnamed__ (2007-10-02 22:48:00) [35]>>Slym ©
Да извиняюсь, я сверху правда не так написал как в исходнике, у меня вчера при себе кода не было :)
__Unnamed__ (2007-10-02 22:23:00) [36]>>Slym ©
Я хотел показать, что я уже перепробовал все, что возможно и порядок обработки сообщений я знаю, но вот как можно правильно обработать ситуацию описанную выше, я просто теряюсь. :)
Поэтому просто КРИЧУУУУУУУ о помощи.......... :)
__Unnamed__ (2007-10-02 22:12:00) [37]>>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:00:00) [38]Два цикла - в смысле два такта входа в цикл с
WaitForMultipleObjects
__Unnamed__ (2007-10-02 21:54:00) [39]>>Сергей М. ©
А что можеш предложить, когда у меня иногда не досылает до 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.
Сергей М. (2007-10-02 09:06:00) [40]
> Может ли прийти FD_CLOSE, если буфер приема еще не пуст?
Может.
> почему Borland в ScktComp везде на операцияx чтения/записи
> использует CriticalSection?
Видимо, просто перестраховался, поскольку в Winsock send/recv-вызовы сами по себе потокобезопасны (по кр.мере на NT-платформе).
С другой стороны, нужно было как-то защитить данные TCustomWinSocket-объектов от потенциальной одновременой их модификации со стороны более чем одного потока при обращении к Send/Receive-методам этих объектов.