-
Сделал сервак на основе сокетов. Использовал 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Кб :\
-
Я извиняюсь за назойливость, коментарии по 18 и 19 будут? :)
-
> как показывает практика еще ниразу не было такого, что описанная > в 18 топике конструкция выдавала бы блоьше 8Кб
Все правильно. Этот дифолт определен опциями SO_SNDBUF и SO_RCVBUF на сторонах передатчика и приемника соответственно.
см. Get/SetSockOpt
-
>>Сергей М.
Я просто уже не знаю что делать, как мне кажеться, WSAEWOULDBLOCK у меня в чтении произойти не может. И в тоже время приходит раньше FD_CLOSE чем опустеет буфер приемника :/.
>>DVM Просто поймите меня правильно, логика основанная на Асинх/Синхр сокетах должна одинаково получать данные от передающего. Если только вы не намекаете на RECV(...)=0 ==FD_CLOSE, но тут возникает вопрос, почему приходит FD_CLOSE раньше, чем освободится буфер.
Я уже наверное всех достал :) Ну не могу я понять этого...!!!!??????
-
> как мне кажеться, WSAEWOULDBLOCK у меня в чтении произойти > не может.
Ну да, не может, ведь ты вызываешь ф-цию recv() как реакцию на событие FD_READ. Но тем не менее, если бы эта "ошибка" фигурировала, в лог ты ее записал бы, хотя ошибкой такая ситуация на самом деле не является.
> почему приходит FD_CLOSE раньше, чем освободится буфер
> сервер не мой
Тогда откуда ты знаешь, что сервер вызывает shutdown ? И тем более откуда ты знаешь, с какаим how-параметром сервер вызывает эту функцию ?
Кстати, рекомендацией Slym © (02.10.07 04:32) [5] ты воспользовался ?
-
Цитата из справки:
If how is SD_SEND, subsequent sends are disallowed. For TCP sockets, a FIN will be sent
Из этого следует, что если партнер по соединению запустил асинхронный send, после чего немедленно выполнил shutdown(SD_SEND), вместо ожидаемого тобой SYN ты запросто можешь получить FIN, что как раз и означает потерю того финального шматка потока, передаваемого тебе партнером.
-
> асинхронный send
Не только асинхронный - и синхронный тоже.
-
1. Сервер Apache но локально(хотя это не имеет значения) 2. Рекомендации Slym-а смотрел см [9], я так и делал просто когда писал вопрос, первое что в голову пришло это было FD_CLOSE :)
Как можно проверить с каким пар-ром ShutDown был выполнен с серверной стороны? Я про это еще нигде не видел примеров и статей :) Я со своей стороны выполняю всегда ShutDown(FServerS, ??_BOTH);
-
Короче, сервер вполне м.б. "кривым".
Ну а твоя "кривизна" как клиента исправляется задействованием WSAEnumNetWorkEvents
-
Ну и CloseSocket после ShutDown соотвественно :)
-
> Как можно проверить с каким пар-ром ShutDown был выполнен > с серверной стороны?
Кроме анализа текста сервера - никак. Кстати, Апач - опенсурсный продукт.
> Я со своей стороны выполняю всегда ShutDown(FServerS, ?? > _BOTH);
Нафига ?
-
> я так и делал
Ну так первым дело м при этом ты должен был искать в списке событий, возвращенных вызовом этой ф-ции, событие FD_READ и только потом FD_CLOSE, но не наоборот.
-
Я столкнулся с такой проблемой, когда я не выполняю ShutDown сам Апаче сервер не закрывает со своей стороны Гнезда и ждет непонятно чего??!!!!! А с ShutDown все нормально :)
-
> когда я не выполняю ShutDown сам Апаче сервер не закрывает > со своей стороны Гнезда и ждет непонятно чего
Быть того не может.
Если ты выполнил без ошибок closesocket, то рано или поздно партнер получит FIN.
-
>>Сергей М. ©
Ну так первым дело м при этом ты должен был искать в списке событий, возвращенных вызовом этой ф-ции, событие FD_READ и только потом FD_CLOSE, но не наоборот.
См. топик [9]!!!
-
Весьма полезным будет также изучить поведение гнезда при его закрытии при взведенной и сброшенной опции SO_LINGER
-
Т.е. при SO_LINGER = TRUE с моей стороны, все данные будут приняты? Или это только для посылки?
-
Это, в свою очередь, зависит от Interval.
-
Подозреваю, что где-то что-то ты недопонимаешь.
Вряд ли Апач будет рвать соединение не передав полностью запрошенные у него данные, если на то нет веских причин.
Пробуй ту же логику в блокирующем режиме. Если все будет ок, то неправ ты, иначе Апач.
-
Скажи, а если у меня происходит скажем задержка потока на 1-5 секунды в связи с записью на устройство. И чтение из буфера приемника задерживается, то по логике TCP FIN прийдет только тогда, когда Апач полностью передаст данные, ну или у него SO_LINGER=False и прийдет дисконнект но в буфере приемника может быть где-то до 8-64Кб информации как я понимаю.
И мне кажеться в моем случае, мне прийдется анализировать <Content-length:?????> + FD_CLOSE и его ошибки. Если при дисконнекте произошли ошибки, то выходить иначе читать до тех пор пока не будет равно ожидаемого.
-
Даже если поступил FIN, данные в буфере приема твоего гнезда, если они там действительно имеются, никуда не денутся, и ты сможешь их прочитать в любой момент до закрытия своего гнезда.
> мне прийдется анализировать > <Content-length:?????>
Да, придется.
-
Удалено модератором
-
Просмотрел бегло, может уже сказано, но не заметил.
Нормально это, FIN пришел вместе с последним пакетом. Сервер закончил запись и вызвал shutdown на свой передающий конец - типа больше ниче передавать не буду. Сокет отправил все из буфера и с последним пакетом - FIN. Получив его, нужно вычерпать все из буфера и закрыть свой приемный конец.
-
Соберем для Вас по сети интернет базу данных потенциальных клиентов для Вашего Бизнеса!!! Соберем данных Много!!! Быстро!!! Недорого!!! Название телефон факс e-mail www адрес имена итд Узнайте подробности по телефону: +79133913837 ICQ: 6288862 Email: rassilka.agent@gmail.com Skype: prodawez
-
Сделай так:
While True do Begin Err:=Recv(...); IF err=SOCKET_ERROR and WSAGetLastError=WSAEWOULDBLOCK Then Break; End; Event - сигнализирует о приходе данных, а IOCTLSocket - покажет всего 8192 макс хотя там может быть и больше :) (проверенно экспериментально) А вообще лучше использовать Select в потоках :) тоже экспериментально :) Я тоже пытался прокси на Эвентах написать, но столкнулся с такой же ситуацией и с не закрытыми соединениями со стороны апачей :). Спасло только то что весь код переделал под синхронные сокеты.
-
При известном количестве данных - читай сколько указано в заголовке, recv вернет ошибку если соединение будет закныто раньше.
В отдельном потоке, можно и блокирующими сокетами обойтись.
-
> FireMan_Alexey (04.02.09 18:29) [43]
А ничего что год прошел? :)
|