![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
TServerSocket, TClientSocket непонятная потеря коннекта
Анонимщик © (2004-07-09 16:01:00)Клиент подключается к серверу, они обмениваются данными, но только до поры, до времени. Вдруг непонятно из-за чего связь даже не разрывается, но сервер данные шлет успешно, а вот клиент их не принимает, даже не входит в процедуру ClientSocketRead.
К сожалению, более внятно спросить не получается.
В какую сторону нужно смотреть?
Анонимщик (2004-07-14 18:46:00) [1]Вполне возможно, что так оно, в конце концов, и будет. Тем более, что это и есть самый правильный метод.
Digitman (2004-07-14 17:05:00) [2]
> Indy и так создает поток для каждого клиента, еще делать
> и поток для взаимодействия с этим потоком
зачем еще один поток-то создавать на каждое соединение ?
это оправдано лишь в случаях, когда ф-ции транспорта и обработки данных, доставляемых транспортом, требуется по каким-то причинам разнести по разным трэдам
в твоем же случае можно тот самый единственный транспортный трэд, который ассоциирован с клиентом, "научить" кроме трансп.ф-ций "слушать" сообщения, адресованные ему в рамках приложения-сервера
разослать в цикле сообщение всем транспортным тредам вызовом PostThreadMessage() - сущий пустяк, никаких ощутимых временных ресурсов это не займет
Анонимщик (2004-07-14 16:48:00) [3]Григорьев Антон ©
Надо было. Но тогда надо было еще и задуматься, зачем, если Indy и так создает поток для каждого клиента, еще делать и поток для взаимодействия с этим потоком. В общем, я, как обычно, не допер вовремя.
Григорьев Антон (2004-07-14 16:21:00) [4]Так надо обрабатывать каждого клиента в своей нити, и тогда медленная связь с одним из них будет только его проблемой.
Анонимщик (2004-07-14 11:56:00) [5]Может, и загнул. А делал следующее: всем заинтересованным в текущих данных клиентам по очереди отсылал одно и то же. Пока сеть локальная, все нормально, как только подключается кто-нибудь через dial-up со своим килобайтом в секунду, так и всем ждать приходится, пока этому клиенту будет отправлена очередная порция данных.
Digitman (2004-07-14 11:07:00) [6]
> Анонимщик © (14.07.04 10:58) [36]
> медленный клиент тормозит всех остальных клиентов и работу
> сервера
что-то ты явно загнул с этим ..
если ты реализовал некорректную/неэффективную логику, то это вовсе не значит, что блокирующий режим есть источник личных твоих бед
Анонимщик (2004-07-14 10:58:00) [7]При использовании блокирующих сокетов меня остановило то, что медленный клиент тормозит всех остальных клиентов и работу сервера.
Григорьев Антон (2004-07-14 09:42:00) [8]
> Анонимщик © (13.07.04 17:56) [33]
А какая у вас главная цель? Если найти решение проблемы, то, ИМХО, лучше блокирующих сокетов вы ничего не придумаете. А если разобраться с буферизацией, то это не так просто. Я до вчерашнего дня был уверен, что понимаю, как и что, так нет, Digitman новые сведения подкинул, которые противоречат тому, что я раньше читал. Буду дальше рыть MSDN.
Digitman (2004-07-13 18:45:00) [9]
> использовал для тех же целей Indy, и никаких совершенно
> проблем не было
так ведь этим проблемам в случае блок.режима (каковой использует Инди) и взяться-то неоткуда .... блок.режим подразумевает организацию сет.ядром ЕЩЕ ОДНОГО промежуточного буфера, размер которого ограничен лишь общесистемными ресурсами на момент вызова .. вот в этот промеж.буфер и копируется все то, что ты передал параметрами в send-вызов ... а уж оттуда порциями по дифолт-размеру ядро последовательно вычерпывает блоки данных и передает их для трансляции
> В локальной сети средняя скорость передачи в случае блокирующего
> режима была почему-то выше, независимо от количества клиентов
как минимум делай скидку на накл.расходы при неблок.режиме ... выигрываешь в удобстве асинхр.режимов - проигрываешь в производительности ... и наоборот ... хотя на сравнительно небольших транслируемых данных (при сравнительно невеликой их интенсивности) это вряд ли заметно ... а если и заметно, то , думаю, оное есть при использовании оконного механизма асинхр.нотификации о трансп.событиях (см. WSAAsyncSelect)
Анонимщик (2004-07-13 17:56:00) [10]Да, насчет блокирующего режима, использовал для тех же целей Indy, и никаких совершенно проблем не было. И еще был один неожиданный и неутешительный для меня момент. В локальной сети средняя скорость передачи в случае блокирующего режима была почему-то выше, независимо от количества клиентов, и даже при использовании только одного компьютера в качестве и сервера, и клиента. Так ничего тогда и не понял.
Digitman (2004-07-13 17:21:00) [11]
> Кстати, насчёт блокирующего режима
а эт другой огород ...
Григорьев Антон (2004-07-13 16:51:00) [12]
> Digitman © (13.07.04 08:23) [27]
>
> > Григорьев Антон © (12.07.04 15:41) [21]
Спасибо за интересную информацию. Как только доберусь до второго компьютера, а также нормального интернета, обязательно проверю. Но даже при беглом обзоре всё выглядит не так однозначно. Вот, например, статья из MSDN'а, в которой прямо указано, что в некоторых случаях в буфер можно положить больше, чем его максимальная длина: http://support.microsoft.com/default.aspx?scid=kb;en-us;214397Правда, там речь только о блокирующем режиме.
Кстати, насчёт блокирующего режима. А может, решить проблему можно как раз его использованием? Тогда не будет никаких WSAEWouldBlock.
Digitman (2004-07-13 11:25:00) [13]
> приемник получает или слипшиеся, или разделенные куски
абсолютно нормальная ситуация
важно что в конечном итоге приемник рано или поздно получит потокданных именно в том виде и в той последовательности, в какой его сформировал передатчик
> начинаешь делать как проще, а потом - разнообразные грабли
) ... "Закон Черномырдина"
Анонимщик (2004-07-13 11:10:00) [14]Спасибо за советы.
Digitman
Кстати, даже на одной машине попытка непрерывно передавать текстовые данные постоянно с помощью SendText в конце концов (и даже довольно быстро, у меня, обычно, и минуты не проходило) приводит к тому, что приемник получает или слипшиеся, или разделенные куски.
А что касается существующих протоколов, то хорошо бы, конечно, но ведь как обычно - начинаешь делать как проще, а потом - разнообразные грабли. Есть, вроде, H231, или что-то такое, но я честно искал описание и реализацию, но ничего дармового так и не нашел. Не исключено, само собой, что плохо искал. Тем более не уверен, что смог бы так просто прикрутить к любому из протоколов нужную мне функциональность, хотя это уже мои предположения.
VID
И тебе спасибо, может, и попробую, если совсем нигде уж более серьезного глюка не найду (есть ведь еще и клиент, его еще в достаточной мере не прологгировал), но у меня есть сомнения к такому способу из-за Анонимщик © (12.07.04 14:45) [20]
Digitman (2004-07-13 08:31:00) [15]
> Григорьев Антон © (12.07.04 15:41) [21]
и - обрати внимание - Борланд неспроста использует в теле метода SensStreamPiece() промежуточную буферизацию блоками по 4к ... кратность размера буфера размеру страницы вирт.памяти бесспорно никогда не помешает, а дифолтные 8к делятся как раз пополам ..
Digitman (2004-07-13 08:23:00) [16]
> Григорьев Антон © (12.07.04 15:41) [21]
однако на тот самый дифолт в 8к многие и "попадают" неожиданно..
ты же сам видишь - сплошь и рядом в "Сетях" вопросы а-ля почему, мол, получается так, что я косой десяток раз подряд вызывал SendText, отправив в общей сложности строк килобайт эдак на 200, а на стороне приемника получил всего лишь одно событие OnRead, где ReceiveLength показал мне что-то около 8к...
когда и приемник и передатчик находятся на одной машине, то этот дифолт как правило не заметен - можно и сотню-другую кб отправить к ряду одним send-вызовом и при этом не получить wsaewouldblok .. ядро эффективно разруливает такую localhost-ситуацию.. но стоит только разнести приемник и передатчик в глоб.сети, как дифолт этот встает во весь рост
> Анонимщик © (12.07.04 14:32) [19]
ну что тебе здесь можно посоветовать ?
во-первых присмотреться внимательно к существующим на сей день real-media-протоколам и кодекам, формирующим поток медиаданных для трансляции по тому или иному трансп.протоколу (в твоем случае - TCP/IP)
во-вторых, если таки хочется поизобретать велосипеды, пересмотри собственный ПИО на предмет введения в передаваемый пакет некоей метки предельно допустимого времени трансляции пакета .. если приемник получив такой пакет обнаруживает, что факт.время приема превысило предполагаемое передатчиком, то пакет попросту отвергается, и если отвержение данного пакета вызывает рассинхронизацию "кадров", приемник медиапотока шлет спецсообщение, информирующее о рассинхронизации с момента получения такого-то последнего корректного медиакадра
VID (2004-07-13 00:39:00) [17]TO: Анонимщик
Если не хочешь использовать событие OnWrite, то можешь использовать функцию WaitForData (совсем немного переделана мною):
function WaitForData(Socket:TSocket; Timeout: Longint; IsWriteMode:Boolean): Boolean;
var
FDSet: TFDSet;
TimeVal: TTimeVal;
begin
TimeVal.tv_sec := Timeout div 1000;
TimeVal.tv_usec := (Timeout mod 1000) * 1000;
FD_ZERO(FDSet);
FD_SET(Socket, FDSet);
IF IsWriteMode then
Result := select(0, nil, @FDSet, nil, @TimeVal) > 0
else Result := select(0, @FDSet, nil, nil, @TimeVal) > 0
end;
Эту функцию используем так: Перед каждым вызовом SendBuf сначала проверяем, способен ли сокет принять данные, для размещения их в очередь на отправку. Вот, смотри:
IF WaitForData(FSocket.SocketHandle, 1000, True) then
begin
...
SendSize := TargetSocketThread.FSocket.SendBuf((@Text[Result+1])^, RemainSize);
...
end else begin {действия если НЕ СПОСОБЕН} end;
Причём, если тебе необходимо во-что бы то ни стало дождаться, и отправить данные, то весь вышеуказанный код примера надо поместить в цикл, где и будет проверяться то, сколько байт отправлено (отправлено = общий размен, то выходим из цикла).
Учти, что в этом случае, твой поток станет глухим и слепым, он зациклится, пока цикл не выполниться. Совет: при второй итерации цикла, вызывай Sleep(1).
Это можно сделать, например, так:
firstiteration := true;
repeat
if firstiteration then firstiteration := false else sleep(1);
until....
В-общем, попробуй это решение, и может твой вопрос в Анонимщик © (12.07.04 14:45) [20] потеряет актуальность.
Polevi (2004-07-12 18:22:00) [18]организуй свою очередь, помещай данные в нее а затем вызывай некую ф-ию F которая будет пытаться отправить очередь до тех пор пока все не отправит, либо до WSA_EWOULDBLOCK
в обработчике OnWrite просто вызывай F ф-ию
Анонимщик (2004-07-12 16:58:00) [19]Я вот чем дальше тестирую, тем реже появляется ошибка. А всего-то вставил после SendBuf():
lastErr := GetLastError;
if lastErr <> 0 then begin
Writeln(fLog, IntToStr(lastErr));
Flush(fLog);
end;
Опять-таки, непонятно почему. Условия вроде как одинаковые.
Но снова хочу повторить [20]:
Ну вот, интересно, я под дебагом поставил условия, чтобы из цикла программа вываливалась, потом, через какое-то время (пару минут), чтобы снова начиналась передача данных. Тот же эффект, SendBuf() возвращает -1. Сеть локальная, передаю себе же, на свой же компьютер, т.е. за две минуты все, что там накопилось, ну точно должно отправится. С чем такое поведение может быть связано?
Конечно, логику работы исправлю, но этот момент все же непонятен.
Григорьев Антон (2004-07-12 16:51:00) [20]
> Так что я не понял, с чем не согласен?
Я не согласен вот с этой фразой:
> Если хочешь узнать размер по дифолту внутреннего буфера
> передачи, на то есть ф-ция GetSockOpt() с соотв. константой-параметром
> ... больше данных чем этот размер ты все равно не сможешь
> "затолкать" в очередь передачи
Затолкать больше вполне реально, т.к. максимальный размер заталкиваемого определяется низкоуровневым буфером, а GetSockOpt даёт размер высокоуровнего. Проверено практикой: через сокет можно отправлять пакеты большего размера, чем тот, который определяется через GetSockOpt.
Но это не значит, что предела нет, он есть, но через GetSockOpt его не узнаешь.