-
Сделал сервак на основе сокетов. Использовал 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. Скажите не потеряется ли быстродействие и независимость самого потока при использовании критических секций. Или же моя ошибка заключается в другом? :)
-
Да когда делал, через оконные функции, то все доходило нормально, но как-то хотелось бы, чтобы все было в потоке :) Заранее спасибо!
-
Может конечно вопрос звучит глупо, но после FD_CLOSE приходит FD_READ в котором доходит оставшийся кусок!!!! Но после FD_CLOSE как положенно можно выходить из цикла и потока, поэтому я и написал сюда, может быть кто-нибудь подскажет. Может нужно использовать критические секции? Режим у сокетов неблокирующий. Просто из описания критических секций я понял, что остальные потоки приостанавливаются для ожидания выполнения самой критической секции. Т.о. независимость каждого потока нарушается, как я понимаю. Все ждут одного????!!!!!! Если я не прав пожалуйста поправте :) И почему Borland в ScktComp везде на операцияx чтения/записи использует CriticalSection?
-
Может кто-нибудь подскажет, что это может быть? Или все будут морозиться...
-
Ответьте на вопрос. Может ли прийти FD_CLOSE, если буфер приема еще не пуст?
-
-
> Может ли прийти FD_CLOSE, если буфер приема еще не пуст?
Может.
> почему Borland в ScktComp везде на операцияx чтения/записи > использует CriticalSection?
Видимо, просто перестраховался, поскольку в Winsock send/recv-вызовы сами по себе потокобезопасны (по кр.мере на NT-платформе).
С другой стороны, нужно было как-то защитить данные TCustomWinSocket-объектов от потенциальной одновременой их модификации со стороны более чем одного потока при обращении к Send/Receive-методам этих объектов.
-
>>Сергей М. © А что можеш предложить, когда у меня иногда не досылает до 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: 14075Connection: closeContent-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.
-
Два цикла - в смысле два такта входа в цикл с WaitForMultipleObjects
-
>>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;
-
>>Slym © Я хотел показать, что я уже перепробовал все, что возможно и порядок обработки сообщений я знаю, но вот как можно правильно обработать ситуацию описанную выше, я просто теряюсь. :)
Поэтому просто КРИЧУУУУУУУ о помощи.......... :)
-
>>Slym © Да извиняюсь, я сверху правда не так написал как в исходнике, у меня вчера при себе кода не было :)
-
а в чем кстати принципиальность использования неблокирующего режима? Всеравно в потоке и с блокировкой (WaitFor)... и без использования готовых компонент. И как ты обрабатываешь FD_WRITE/FD_READ - ведь операция записи/чтения может оборваться WSAEWOULDBLOCK и при следующем FD_WRITE/FD_READ нужно дописывать/дочитывать оставшиеся данные а не все. может в этом затык?
-
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;
-
> __Unnamed__
Если все равно создал доп поток то не проще ли использовать блокирующие сокеты, а не асинхронные сокеты на событиях?
-
> что можеш предложить
А какое отношение к этому имеют CriticalSections ?
И где у тебя реакция на WSAGetLastError = WSAEWOULDBLOCK ?
-
Извиняюсь но в чтении разве может быть WSAEWOULDBLOCK? И если оно есть, то что оно означает?
-
> Извиняюсь но в чтении разве может быть WSAEWOULDBLOCK?
Может. Ты хотя бы справку то по recv() открыл бы.
-
Ща открываю :) Вот цитирую: 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; В этом куске кода я проверяю кол-во пришедших байт и не пытаюсь закачать больше!
-
Единственное я не проверял CountR>8192 (Max Receive) Возможно там больше, но как показывает практика еще ниразу не было такого, что описанная в 18 топике конструкция выдавала бы блоьше 8Кб :\
|