![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Странности у TTcpClient и TTcpServer
SpellCaster © (2006-07-04 19:52:00)Товарищи! Для решения глобальной задачи написал простенький тестер. Есть TTcpClient и TTcpServer. Сервер по таймеру пишет в поток данные (допустим, текущие секунды), а клиент постоянно в цикле читает из этого буфера. Так вот, по идее после первого прочтения буфер должен быть пуст - но нет, там остается второй байт, извлекаемый из буфера только при втором прочтении. При этом количество прочитанных байт (TcpClient1.ReceiveBuf(Buf,size) ) всегда почему-то равно 255. Как можно проверить целостность полученного сообщения, если потенциально его размер неизвестен?
И еще. Пытаюсь "навесить" серверу события, типа OnAccept, но из всех вызывается почему-то только CreateHandle. Причем если событиям присвоены обработчики, при закрытии формы возникает Exception, без обработчиков все нормально. Это что-то у меня глючит, или как?
Заранее спасибо.
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1Timer(nil);
Server1.Active:=True;
Client1.Active:=True;
Client1.Connect;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
s:=inttostr(SecondOf(Time));
StrCopy(PChar(Buf),PChar(s));
size:=Length(s);
Label1.Caption:=s;
Server1.SendBuf(Buf,size);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Buf:=PByte(AllocMem(MaxMsgLen));
end;
procedure TForm1.Button2Click(Sender: TObject);
var
msg: array of Byte;
i: byte;
begin
i:=client1.ReceiveBuf(Buf,size);
SetLength(msg,3);
msg[0]:=Buf^;
inc(buf);
msg[1]:=Buf^;
msg[2]:=0;
log(PChar(@msg[0]));
log(inttostr(i));
end;
Сергей М. (2006-07-12 15:51:00) [1]
> буду юзать блоки
Гениальное решение.
SpellCaster (2006-07-12 12:54:00) [2]ОК, буду юзать блоки. Всем спасибо за инфу!
Сергей М. (2006-07-12 08:17:00) [3]
> SpellCaster (11.07.06 17:40) [37]
> этот цикл обработки - по типу как в прогах на голом win
> API?
Именно так.
Slym (2006-07-12 05:41:00) [4]SpellCaster (11.07.06 15:39) [35]
стоит ли мучить неблок дальше или остаться с блоком
Неблокирующие нотификация реализована на сообщениях окнам,
Эти сообщения может обрабатывать один единственный поток - главный поток программы, и доп. потоков не требуется. Все операции встают в очередь (очередь сообщений) т.к. обрабатываются одним потоком, отсюда - если попадется длительная операция подвиснит весь сервер, все другие клиенты.
Блокирующий режим может остановить выполнение потока, поэтому все блокирующие операции оборачиваются в отдельный поток, дабы не тормозить другие операции. Все операции выполняются в отдельных потоках, что не вызывает "подвис" основного потока так и других потоков. Все клиенты одновременно могут выполнять длительные операции.
SpellCaster (2006-07-11 17:40:00) [5]Я имею в виду, применительно к сокетам.
А что, этот цикл обработки - по типу как в прогах на голом win API?
Сергей М. (2006-07-11 15:47:00) [6]
> SpellCaster (11.07.06 15:39) [35]
> нигде ничего не было про цикл выборки сообщений
Как думаешь, для чего нужен метод Application.Run ?
SpellCaster (2006-07-11 15:39:00) [7]Slym
Сергей М.
Я смотрел все статьи с Delphi World по этой теме, и нигде ничего не было про цикл выборки сообщений. Есть где-нибудь пример?
> Сделай сквозной поиск по scktcomp.pas фразы "AllocateHwnd"
> и удивись.
Мда... TCustomWinSocket фактически представляет собой окно...
> У этого режима так же как у неблокирующего есть свои преимущества
> и недостатки.
> Выбор того или иного режима следует делать осознанно.
А если можно, вкратце - какие? Просто они оба мне сейчас одинаковы, хотелось бы не ошибиться в выборе и уже определить, стоит ли мучить неблок дальше или остаться с блоком. Только, пожалуйста, не надо отсылать меня к каким-нибудь толстым книжкам и длинным статьям...
Сергей М. (2006-07-11 11:28:00) [8]
> почему в "сетевой" ОС linux нет неблокирующих сокетов?
В Линухе иной событийный механизм, нежели в Win32. Он базируется на т.н. "сигналах". Сокеты как таковые тут ни при чем.
> неблокирующего, на мой взгляд, одни недостатки
Позволю себе не согласиться с этим.
Slym (2006-07-11 11:22:00) [9]Сергей М. © (11.07.06 8:27) [32]
У этого режима так же как у неблокирующего есть свои преимущества и недостатки.
У неблокирующего, на мой взгляд, одни недостатки, а если не так, то почему в "сетевой" ОС linux нет неблокирующих сокетов?
Сергей М. (2006-07-11 08:27:00) [10]
> SpellCaster (10.07.06 18:16) [28]
> я уже больше склоняюсь к блокирующему
У этого режима так же как у неблокирующего есть свои преимущества и недостатки.
Выбор того или иного режима следует делать осознанно.
> откуда ты это откопал? Я смотрел в исходниках, там вроде
> нет ничего связанного с окнами
Сделай сквозной поиск по scktcomp.pas фразы "AllocateHwnd" и удивись.
> в том-то и весь фикус, что никаких ошибок не возникает
В неблок.режиме и не возникнет, потому что у тебя отсутствует цикл (см. [24], [29], [30]).
Slym (2006-07-11 07:33:00) [11]Slym © (11.07.06 7:29) [30]
а где цикл выборки сообщений?
Привентивный удар:
Цикл выборки сообщений главного потока не получит эти сообщения, т.к. клиентский сокет у тебя создан в контексте другого потока (хендл окна для сообщений тоже) то и сообщения будут направляться в дочерний поток.
Slym (2006-07-11 07:29:00) [12]Пользуешь неблокируемый режим который базируется на сообщениях, а где цикл выборки сообщений? нету... вот тебе сообщение и не приходит... т.е. сообщение об Active=true тебе не придет.
если уж подсел на блокирующий режим так и сиди на нем он проще в программировании
Ketmar (2006-07-10 19:09:00) [13]>SpellCaster (10.07.06 18:16) [28]
> отследить разрыв связи там проще: не поступает данных какое-
> то время - значит, обрыв.
ещё проще. об обрыве расскажет сама recv() -- результатом. %-)
вообще, никогда не использовал неблокирующий режим. какой-то он, имо, неестественный.
SpellCaster (2006-07-10 18:16:00) [14]
> Ну и не вешай ! Но при этом придется задействовать блокирующий
> режим.
> И "главный модуль" твой тут вообще ни причем.
Да, я уже больше склоняюсь к блокирующему.
> При том что в режиме ctNonBlocking Борландом задействуется
> асинхронная нотификация о событиях гнездового транспорта
> именно с использованием окон и оконных сообщений.
А откуда ты это откопал? Я смотрел в исходниках, там вроде нет ничего связанного с окнами.
> Безразлично где он "лежит".
Это я к тому, что уж у него-то событие должно бы сработать. Но - не срабатывает.
> Значит нет подключения. поэтому и не "вызывается".
> Причины отказа ищи в параметре ErrorСode события TClientSocket.
> OnError (для режима ctNonBlocking) или в сообщении об исключении,
> возникшем при выполнении ClientSocket.Open (для режима
> ctBlocking)
Так в том-то и весь фикус, что никаких ошибок не возникает!!! В противном случае я бы попробовал что-то разрулить, а тут просто выполняется строкча соединения, но с нулевым результатом, и все! Никаких событий не вызывается.
> А кто тебе мешает сделать коннект в главном потоке и по
> событию ОНКОННЕКТ прикрутить дополнительный поток работающий
> с этим сокетом (режим блокированный).
Ну в принципе, блокирующий и так нормально фурычит. А коннект... нужно предусмотреть возможность утсановки повторного подключения, если был обрыв связи. Логично это засунуть в код потока и постоянно проверять доступность канала. В принципе, можно извратиться и сделать проверку подключения в потоке, а сам коннект в главном модуле. Или попробовать просто synhronize(thread.connect).
Вообще, мне нет особой разницы, какого типа сокеты юзать. Просто блоки мне кажутся более естественными, да и отследить разрыв связи там проще: не поступает данных какое-то время - значит, обрыв.
medved_68 (2006-07-10 17:05:00) [15]
> SpellCaster
А кто тебе мешает сделать коннект в главном потоке и по событию ОНКОННЕКТ прикрутить дополнительный поток работающий с этим сокетом (режим блокированный).
Сергей М. (2006-07-10 16:53:00) [16]
> у меня должно быть несколько одновременных подключений к
> разным сервакам. Потоки должны будут принимать инфу и сливать
> ее в файлы
Все это с успехом реализуется в одном-единственном (основном) потоке.
Если , конечно же, на основной поток не возложены при этом какие-либо "обязанности", кроме интерактивного взаимодействия с юзером.
> Вариант создавать несколько экземпляров сокет-клиентов в
> главном модуле и вешать на них события меня как-то не очень
> прельщает
Ну и не вешай ! Но при этом придется задействовать блокирующий режим.
И "главный модуль" твой тут вообще ни причем.
> при чем тут оконные сообщения?
При том что в режиме ctNonBlocking Борландом задействуется асинхронная нотификация о событиях гнездового транспорта именно с использованием окон и оконных сообщений.
> Сервак у меня лежит визуально на форме
Безразлично где он "лежит".
> у него есть событие КлиентКоннект. При подключении из потока
> это событие не вызывается
Значит нет подключения. поэтому и не "вызывается".
Причины отказа ищи в параметре ErrorСode события TClientSocket.OnError (для режима ctNonBlocking) или в сообщении об исключении, возникшем при выполнении ClientSocket.Open (для режима ctBlocking)
SpellCaster (2006-07-10 16:40:00) [17]Ketmar, у меня сервер и клиент в одной проге, соединяются по 127.0.0.1. Задержек быть не должно вроде бы )). К тому же я ставил sleeP(2000) - та же шняга.
>Сергей М.
> Так ты что, всю эту клиентскую петрушку в доп.потоке пытаешься
> сотворить ?
> Вопрос - зачем ?
Потому что у меня должно быть несколько одновременных подключений к разным сервакам. Потоки должны будут принимать инфу и сливать ее в файлы. Вариант создавать несколько экземпляров сокет-клиентов в главном модуле и вешать на них события меня как-то не очень прельщает.
> Разумеется, ни одно событие в доп.потоке не возникнет, пока
> ты не организуешь там цикл ожидания/выборки/диспетчеризации
> оконных сообщений !
Не понял, при чем тут оконные сообщения? События вызываются в коде TCustomWinSocket-а, и им чихать на окна.
Причем мне-то они пока и не нужны! Я просто пытаюсь подключиться к серваку. Сервак у меня лежит визуально на форме, у него есть событие КлиентКоннект. При подключении из потока это событие не вызывается, а при подключении из кода основного модуля (в баттон.Онклик) - всё путём.
Сергей М. (2006-07-10 15:37:00) [18]
> ОнКоннект не вызывается. Сеодинения не происходит
Если OnConnect не происходит, то должно произойти OnError, где как раз и можно выяснить причину отказа.
> Можно, в принципе, соединяться из главного потока проги
Так ты что, всю эту клиентскую петрушку в доп.потоке пытаешься сотворить ?
Вопрос - зачем ?
Разумеется, ни одно событие в доп.потоке не возникнет, пока ты не организуешь там цикл ожидания/выборки/диспетчеризации оконных сообщений !
Ketmar (2006-07-10 15:34:00) [19]есть подозрение, что коннект долго устанавливается. а так как режим неблокирующий, то...
SpellCaster (2006-07-10 15:26:00) [20]ОнКоннект не вызывается. Сеодинения не происходит.
> Значит режим - неблокирующий.
А разве это св-во играет роль только в блоке?
Попробовал коннектиться так:
fClient.Socket.Connect(fClient.Socket.SocketHandle);
ClientConnect выполняется, однако на сервере список соединений пуст (Server.Socket.ActiveConnections).
Можно, в принципе, соединяться из главного потока проги; просто как-то не хочется выносить это из кода потока. Да и просто неясно, почему не фурычит коннект из потока.