Форум: "Сети";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];
ВнизТочное определение момента, когда можно отправлять текст в сокет Найти похожие ветки
← →
VID © (2004-05-31 02:12) [0]Я долго мучался, но так ничего и не смог придумать. Дело такое:
необходимо от сервера клиенту послать ну скажем 1000 сообщений (через Socket.SendText), причём длина отправляемого текста - большая.
В-общем получается что то вроде
for i := 1 to 1000 do Socket.SendText(S);
Так вот до клиента приходит как правило только половина отправленных сообщений.
Насколько я понимаю, такой подход конечно не верен, т.к. разумно было бы все эти 1000 отправляемых текстов сложить в один огромный текст, и отправить его зараз, а сокет сам уже разобрался что бы что и как сделать что бы всё дошло до клиента. Но, к сожалению, я не могу сделать так, ибо постановка задачи предполагает только вышеуказанный вариант.
Как я понимаю решение проблемы должно выглядеть следующим образом:
for i:=1 to 1000 do
begin
Socket.SendText(S);
{и здесь ждём столько времени сколько необходимо что бы не возникло ситуации с перегрузкой вызовами этого метода }
end;
Так вот я не могу использовать указанный вариант решения проблемы, потому что не могу определить сколько именно времени дать простаивать приложению что бы не возникло перегрузки.
Брать какие то константы типа
x:=gettickcount;
while gettickcount - x < 100 do application.processmessages;
я не хочу, т.к. хоть это и срабатывает на моём компе, но не факт что сработает на более медленном.
как быть ?
← →
Piter © (2004-05-31 02:36) [1]Что за компонент Socket?
← →
Piter © (2004-05-31 02:36) [2]И какой режим сокетов используется? Блокирующий или не блокирующий?
← →
VID © (2004-05-31 02:59) [3]Блин, ну неужели я догадался !:)))
решил так:
содрал функцию WaitforData из исходников TWinSocketStream и немного переделал её:
function WaitForData(Socket:TSocket; Timeout: Longint): 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);
Result := select(0, @FDSet, @FDSet, nil, @TimeVal) > 0;
end;
сразу видно что я переделал :)
а дальше:
for i := 1 to 1000 do
if waitfordata(socket.sockethandle, 10000) then
socket.sendtext(s);
и всё...
слава богу, справился :)
PS: Piter, сервер неблокирующий, а Socket это объект типа TCustomWinSocket который создаётся для каждого нового коннекта сервером (TServerSocket) автоматически.
← →
Piter © (2004-05-31 03:29) [4]VID (31.05.04 02:59) [3]
сервером (TServerSocket) автоматически.
TServerSocket? Неблокирующий режим? Хех, что ты фигню городишь?
Зачем тебе спрашивается события OnClientWrite?
← →
Piter © (2004-05-31 03:32) [5]Делаешь так:
вызываешьSocket.SendText
он тебе возвращает сколько символов отправлено.
1)Если отправлено столько, сколько передавал - все нормально, идешь дальше.
2) если отправилось меньше - то запоминашь какие символы не отправились и отправляешь их при возниконовении события OnClientWrite
← →
Evgeny V © (2004-05-31 05:47) [6]
> Piter © (31.05.04 03:32) [5]
> Делаешь так:
>
> вызываешь Socket.SendText
>
> он тебе возвращает сколько символов отправлено.
>
> 1)Если отправлено столько, сколько передавал - все нормально,
> идешь дальше.
> 2) если отправилось меньше - то запоминашь какие символы
> не отправились и отправляешь их при возниконовении события
> OnClientWrite
Cогдасен, но увы у меня вот OnClientWrite не всегда возникает, один раз только
(и не только у меня, но и у коллег на работе та же проблема), пробовал на просто АПИ сокетах в модели EventSelect, то же самое, FD_WRITE возникает на моей машине только один раз, при первом входе в WSAWaitForMultipleEvents. Это и в дельфи 5 и в дельфи 6 наблюдается с клиентским и серверным сокетоми В ОС 98 и XP. Интересно было бы, что бы кто подсказал в чем проблема? Я это дело конечно обошел через перекрытый ввод/вывод, WsaSend(FSocket,@WBufferW,1,ByteWr,FlagsW,@WriteE,nil); где WriteE - OVERLAPPED структура, по чтению так же делаю, хотя событие на него возникает нормально и регулярно. Так и вот и смещиваю две модели, EventSelect для событий FD_CLOSE, FD_CONNECT и OVERLAPPED для событий чения и записи. Кто знает причину - поделитесь, буду признателен:-)
← →
atruhin © (2004-05-31 06:01) [7]>Evgeny V - на сайте есть куча стотей по сокетам, отдельно по компонентам TClienSocket, TServerSocket. Почитай а то у тебя такая каша получилась.
← →
Verg © (2004-05-31 06:28) [8]Ну так и отправляй, пока тебе не скажут "хватит", т.е. пока Send*** не вернет SOCKET_ERROR, а WSAGetLastError == WSAEWOULDBLOCK, после чего надо дождаться OnWrite и продолжить передачу.
К примеру, разбери по исходникам ScktComp как работает метод SendStream...
← →
Evgeny V © (2004-05-31 06:53) [9]
> atruhin © (31.05.04 06:01) [7]
> >Evgeny V - на сайте есть куча стотей по сокетам, отдельно
> по компонентам TClienSocket, TServerSocket. Почитай а то
> у тебя такая каша получилась.
Угу каша, но работает стабильно, что конечно не является оправданием. Читал , но видно что проглядел. Так что если можно конкретную ссылку, потому как следущий код
procedure TForm1.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
begin
Inc(pl);// глобальная интежер переменная
Socket.SendText(IntToStr(pl));
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
Edit1.Text:=Socket.ReceiveText;
end;
Срабатывает один раз, и в эдите вижу только 1(да и с точкой останова только раз попадаю в OnWrite):-)) Cокет в неблокируещем режиме, адрес 127.0.0.1
И так вопрос, ссылку если можно на описание проблемы или как правильно делать, или указать в чем не точность.
← →
Verg © (2004-05-31 07:08) [10]
> [9] Evgeny V © (31.05.04 06:53)
Ё... так говорят же. После установления соединения, первый же OnWrite говорит от том, что соект готов к передаче данных. После этого и до разрыва соединения передавайте сколько влезет, пока не возникнет SOCKET_ERROR (т.е. sendtext(....)=SCOKET_ERROR). При этом, если WSAGetLastError будет равен WSAEWOULDBLOCK, то значит в передающем буфере сокета не осталось св. места, но как только оно появится вам сообщат событием FD_WRITE (OnWrite).
Сколько же повторять одно и то же можно....
← →
Evgeny V © (2004-05-31 07:28) [11]
> Verg © (31.05.04 07:08) [10]
>
> > [9] Evgeny V © (31.05.04 06:53)
>
>
> Ё... так говорят же. После установления соединения, первый
> же OnWrite говорит от том, что соект готов к передаче данных.
> После этого и до разрыва соединения передавайте сколько
> влезет, пока не возникнет SOCKET_ERROR (т.е. sendtext(....)=SCOKET_ERROR).
> При этом, если WSAGetLastError будет равен WSAEWOULDBLOCK,
> то значит в передающем буфере сокета не осталось св. места,
> но как только оно появится вам сообщат событием FD_WRITE
> (OnWrite).
> Сколько же повторять одно и то же можно....
Повторять -пока не поймут, теперь понял, спасибо:-)
Но на мой взгляд не очень красиво. Зачем долбить до ошибки, если я точно могу знать момент, когда можно передавать данные, разве что, что бы ставить лданные в очередь на передачу, не дожилаясь пока уйдут предыдущие, ну имеет смысл. Я понял мысль, можно передавать пока не вернет ошибку, и тогда появится OnWrite второй раз, что и появилось, спасибо за ответ
← →
Verg © (2004-05-31 07:49) [12]Все зависит еще и от того, какими порциями и как часто ты пополняешь буфера TCP-передатчика, от текущей ситуации в дрйверах канального уровня, от текущего значения RTT, от количества коллизий на ethernet, от нагруженности каналов, маршрутизаторов, от текущего состояния, производительности принимающей стороны и т.д. и т.п.....
Вы еще не забывайте, что даже если и ошибки WSAEWOULDBLOCK не возникло, то это еще не значит, что ВСЕ ваши данные, указанные в send*** (sendtext в данном случае) были помещены в передающий буфер сокета. Результатом send является integer число, и если оно положительно, то отображает количество помещенных в передатчик байтов из тех, которые вы заказали. Проверять результаты функций семейства send - это ОБЯЗАТЕЛЬНОЕ дело.
> Socket.SendText(IntToStr(pl));
Не думайте, что обязательно вся строка IntToStr(Pl) была взята передатчиком - ни кто не обещал!
← →
Piter © (2004-05-31 11:03) [13]Piter (31.05.04 03:32) [5]
2) если отправилось меньше - то запоминашь какие символы не отправились и отправляешь их при возниконовении события OnClientWrite
Evgeny V, вот я уже написал выше. Если буфер заполнится, то обязательно возникнет OnWrite, а если он не заполнен... так в чем же дело - посылай дальше...
← →
Роман (2004-05-31 11:13) [14]см. исходники ICS
← →
VID © (2004-05-31 19:00) [15]Piter © (31.05.04 03:32) [5]
А чем WaitForData плох ? Если я буду использовать TServer.Socket.OnCLientWrite , то это будет ещё одним слабым местом, которое будет тормозить работу основного потока, а именно в основном потоке у меня созадётся сервер. С другой стороны WaitForData тормозит деятельность именно того потока в котором был вызван - а значит - дополнительного... хм... видимо здесь я должен кое что рассказать об особенностях своего сервера.
Он у меня при каждом коннекте клиента создаёт поток который обслуживает соообщения приходящие от клиента. Сервер получился гибридом обычного NonBlocking и ThreadBlocking сервера. Т.е. приходит от клиента сообщение. Генерируется событие OnClientRead и пришедший текст тут же отправляется в поток (ссылка на который находится в Socket.Data) который обслуживает именно этот сокет, сгенерироваший OnclientRead. ну а дальше, всё уже творится в потоке, обслуживающем клиента. Там же и возникает необходимость массовой отправки пакетов клиенту. Там же и используется WaitForData.
Так что, Piter, проблем нет, всё работает отлично :)
← →
VID © (2004-05-31 19:00) [16]Piter © (31.05.04 03:32) [5]
А чем WaitForData плох ? Если я буду использовать TServer.Socket.OnCLientWrite , то это будет ещё одним слабым местом, которое будет тормозить работу основного потока, а именно в основном потоке у меня созадётся сервер. С другой стороны WaitForData тормозит деятельность именно того потока в котором был вызван - а значит - дополнительного... хм... видимо здесь я должен кое что рассказать об особенностях своего сервера.
Он у меня при каждом коннекте клиента создаёт поток который обслуживает соообщения приходящие от клиента. Сервер получился гибридом обычного NonBlocking и ThreadBlocking сервера. Т.е. приходит от клиента сообщение. Генерируется событие OnClientRead и пришедший текст тут же отправляется в поток (ссылка на который находится в Socket.Data) который обслуживает именно этот сокет, сгенерироваший OnclientRead. ну а дальше, всё уже творится в потоке, обслуживающем клиента. Там же и возникает необходимость массовой отправки пакетов клиенту. Там же и используется WaitForData.
Так что, Piter, проблем нет, всё работает отлично :)
← →
Piter © (2004-05-31 19:03) [17]VID (31.05.04 19:00) [16]
Так что, Piter, проблем нет, всё работает отлично
ну ладно, мне то что. Делай как хочешь
← →
Verg © (2004-05-31 19:36) [18]
> А чем WaitForData плох ?
В той реализации, которая в [3] - это ошибка, а если речь идет про асинхронные режимы сокетов, то вообще полная ахинея.
Там смешаны два FDSET-а: и read и write. Так что, для блок-неблок режимов select там будет давать мягко говоря неверные результаты. А если сокеты еще и асинхронные, то:Note The select function has no effect on the persistence of socket events registered with WSAAsyncSelect or WSAEventSelect.
(С) MSDN
← →
VID © (2004-05-31 20:22) [19]Verg © (31.05.04 19:36) [18]
Да. Просто в тот момент (в три часа ночи), я был рад тому что вообще нашёл метод. А теперь я уже скорректировал код этой функции WaitForData таким образом что туда можно передать параметр указывающий какой FDSet проверять: Read или Write.
И при отправке сообщений вызываю эту функцию с параметром для Write FDSet.
To Piter: ты случаем не в обиде ? :) А то может ты меня не так понял, я только рад что ты сразу же откликнулся на призыв о помощи в этой ветке :)
← →
Piter © (2004-05-31 21:40) [20]VID (31.05.04 20:22) [19]
ты случаем не в обиде
нет, что ты. С чего это вдруг :)
← →
Verg © (2004-05-31 22:40) [21]
> [19] VID © (31.05.04 20:22)
Хорошо, надеюсь, что ф-цию ты поправил. Просто интересно видеть каким образом ты ее применяешь, чтобы избежать того, что будет "слабым местом, которое будет тормозить работу основного потока". За счет чего ты "ускоряешь" работу главного потока?
Перед отправкой ты вызваешь эту (WaitForData, а точнее WaitForWritable) ф-цию, и в один прекрасный момент она тебе указала на невзможность передачи никакой, сколь угодно малой порции инфорамции. И что дальше ты делаешь, чтобы не тормозить работу основного потока? Ведь отправить-то нужно, а "низя", но и поток тормозить нельзя, зависнув на циклическом вызове WaitForData, ожидая когда появится возможность отправки - у потока есть и другие "обязанности"... Дать системе самой сообщить о появлении возможности отправки ты отсек, исключив использование OnWrite (FD_WRITE)... так как же быть? ;)
← →
Piter © (2004-05-31 23:27) [22]мне тоже интересно...
← →
VID © (2004-06-01 01:09) [23]Хороший вопрос! Я честно говоря, не так серьёзно относился к необходимости ВО ЧТО БЫ ТО НИ СТАЛО ОТПРАВИТЬ ИНФОРМАЦИЮ В СОКЕТ ЕСЛИ ПОТРЕБОВАЛОСЬ, но вообще-то ты сам мне дал ответ !
Да, я именно зациклюсь на постоянном вызове WaitForData , и меня совершенно не будет беспокоить то что зациклюсь возможно очень на долго. Почему ? Да просто потому что в описанной в моём первом посте ситуации текст в сокет у меня отправляется исключительно в дополнительном обслуживающем потоке, который создаётся для каждого клиента.
И если этот поток зациклится на WaitforData - то значит так тому и быть, значит такова судьба этого клиента - меня это соверщенно не волнует.
А цикл вообще будет такой
.......
Repeat Until (WaitForData) or Terminated or (not FSocket.Connected)
IF (not Terminated) and (Fsocket.connected) then
FSocket.SendText(...);
← →
Verg © (2004-06-01 02:05) [24]
> Я честно говоря, не так серьёзно относился к необходимости
> ВО ЧТО БЫ ТО НИ СТАЛО ОТПРАВИТЬ ИНФОРМАЦИЮ В СОКЕТ ЕСЛИ
> ПОТРЕБОВАЛОСЬ
Но вообще-то, из темы ветки и из дальнейших твоих реплик создавалось именно такое ощущение.
И еще немного напоминало войну с ветряными мельницами....
> И если этот поток зациклится на WaitforData - то значит
> так тому и быть, значит такова судьба этого клиента - меня
> это соверщенно не волнует.
Тут я уже немного потерял ход твоих мыслей...
Болкирующий-неблокирующий-асинхронный режимы - все имеют место быть и все хороши сами по-себе, НО, "клиниться" на первых двух - это либо от недопонимания прелестей третьего, либо от желания кросс-платформы (точнее, чего уж там - кросс-Linux, какая уж тут кросс.... при Delphi). Хм... скажем замена циклов на конечные автоматы - это проблема? Я чего-то, на самом деле не врубаюсь - в чем собственно проблема?
Между прочим, один из самых популярных в мире Delphi сетевых движков нынче - Indy, базируется именно и только на блокирующем режиме сокетов - это просто, это понятно, особенно после практики файлового ввода-вывода - синхронного, но быстрого априори... просто, тупо и очевидно. Ожо какая там очевидность. Блин, бились, бились, значит WinAPI-шники, асинхронность в лучшем виде - нате вам, пользуйтесь, так нет, нам это "глючно", мы того "знать не хотим", нам надо, чтобы вызванная операция обязательно была закончена на следующей строчке кода после вызова соотв. ф-ции. Мы ее зациклим лучше, если надо, нам так просче, нам так понятнее... а на мультизаладачность... а ну ее. Засовываем все в потоки, не всегда понимая как и что творим, так как мультипоточность требует еще одной ступени понимания, но мы почему-то крутим циклы while not Terminated do if flag then continue.... или на крайняк вся и все в Synchonize и все готово или Antifreez - панацею бросим на форму....
Вопросы, блин, спать не дают потом - а почему загрузка проца - 100 прОцентов? Или почему иногда все слетает к едрене фене в AV?
Но ничего, главное - OnWrite не обслуживать.
Так. Мысли вслух....
← →
VID © (2004-06-01 03:42) [25]Verg © (01.06.04 02:05) [24]
Нет это не мысли вслух! Это откровенный наезд на меня. За что ??
Ты удивишься, но нету у меня 100% загрузки проца, просто потому что я знаю что в любом потоковом цикле надо вставлять строчку sleep(1) и проблем не будет. а ещё как ни странно у меня почему то не было в разрабюатываемом сервере страшных AV от которых всё слетало нафиг. Наверное потому что я аккуратно отношусь к чтению/записи свойств объектов, которые not thread-safe.
Это не значит что я повсеместно ввожу Synchronize() чуть ли не на сам TThread.Execute... Просто... ты уж не огорчайся, я имею представлению, представляешь, имею я представление о том что творится в коде моего сервера , как в основном потоке так и в дополнительных.
Вообще мы отклонились от темы. Сабж решён, я уже давно шагнул далеко вперёд в разработке сервера, так что не надо тут...
Verg, ты у нас мастер. Из уважения к этому, я оставляю право последнего слова за тобой.
А я в этой ветке умолкаю.
← →
Digitman © (2004-06-01 08:53) [26]
> VID
на обиженных воду возят)
напрасно ты воспринимаешь критику так остро и не прислушиваешься к трезвым и взвешенным советам
ты зациклился на select() и видеть не желаешь ничего, что касается FD_WRITE-события, хотя о кроссплатформенности речи не ведешь
ничто не мешает использовать сервер в режиме stThreadBlocking и при этом определять момент освобождения передающего буфера по событию FD_WRITE без блокировки ожидающего это событие потока
на то есть ф-ции WSACreateEvent, WSAEventSelect, WSAWaitForMultipleObjects/MsgWaitForMultipleObjects, WSAEnumNetworkEvents
при использовании этих ф-ций можно организовать цикл ожидания так, что код.поток не будет "глух" в момент ожидания, т.е. находясь в kernel-time будет способен немедленно реагировать как на любые требуемые FD_XXX-события, так и на сообщения, адресованные потоку
WSACreateEvent
WSAEventSelect
while not Terminated and Connected do
case MsgWaitForMultipleObjects of
WAIT_OBJECT_0:
... WSAEnumNetworkEvents - перечисление и обработка транспортных событий гнезда
WAIT_OBJECT_0 + 1:
... выборка/обработка сообщений потоку
end
← →
Verg © (2004-06-01 10:53) [27]
> [25] VID © (01.06.04 03:42)
> Verg © (01.06.04 02:05) [24]
>
> Нет это не мысли вслух! Это откровенный наезд на меня. За
> что ??
Ну что тут скажешь?!
Да как знаешь - тебе видней.
> [17]
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2004.08.01;
Скачать: [xml.tar.bz2];
Память: 0.55 MB
Время: 0.036 c