-
Доброе время суток!
На работе потребовался Messager.
Конечно можно было бы воспользоваться готовыми программами коих тысячи в нете, но мне захотелось попробовать сделать самому.
На сервере должен быть список зарегистрированных пользователей типа так:
TClientState = (csOffline,csOnLine,csOut,csDontDistrab,csInVisible,csUser);
TClient = class(TObject)
Nick,
Login,
Pass,
ip : string;
State : TClientState;
Connection : TIdTCPConnection;
Contacts : TList;
public
Constructor Create;
Destructor Destroy; override;
end;
var
Clients:TList
Кроме того существует список
Действие клиента примерно следующие:
1.соединение
2.
а) если вход, то отправка логина и пароля
б) если регистрация, то отправка рег.данных.
4. обмен данными
5. disconnect при закрытии формы или если клиент выбрал статус Offline
при каждом обращении клиента к серверу, (посылка различных комманд, запрос различной инф. и пр.) посылается заголовок:
THeader = record
ProtocolVersion,
Command,
Encrypt,
Reserved1:byte;
Reserved2:Dword;
BufSize:Dword;
end;
затем если необходимо посылаются какие то данные. размером в BufSize;
Вот событие Execute на сервере:
procedure TForm1.TCPExecute(AContext: TIdContext);
var
buffer:TidBytes;
header:THeader;
ErrMsg:string;
begin
try
AContext.Connection.Socket.ReadBytes(Buffer,HeaderSize);
except
on e: Exception do
begin
if (e.message<>'Read timed out.') then
begin
end;
exit;
end;
end;
header:=BytesToHeader(Buffer);
if not ValidHeader(header,ErrMsg) then
begin
exit;
end;
if Acontext.Data=nil then
begin
UnRegisterResponse(Header,AContext);
end else
if TObject(Acontext.Data) is TCLient then
begin
Response(TClient(Acontext.Data),Header);
end;
end;
Вот здесь мне нужна помощь.
Что в привиденном коде неверно?
Насколько я понимаю процедуры UnRegisterResponse и Response проходят в контексте этого же потока.
Возможно ли в них обращение к списку Clients (TList) без синхронизации?
Как правильно завершить соединение с клиентом?
Например в UnRegisterResponse проходит идентификация пользователя, т.е в цикле перебираются все TClient из списка Clients (TList) на проверку логин/пароль
в случае, если логин/пароль неверны нужно послать сообщение пользователю и разорвать соединение.
Заранее благодарен.
-
OnExecute обрабатывает конкретную нить конкретного подключения.
Зачем и почему здесь потребовалось
т.е в цикле перебираются все TClient из списка Clients (TList) на проверку логин/пароль ?
PS дунотдисторб написано с ошибкой.
PPS забей, займись чем-нибудь не столь бесполезным.
-
> OnExecute обрабатывает конкретную нить конкретного подключения.
Это я понимаю.
> Зачем и почему здесь потребовалось
Не понял вопроса! Что потребовалось?
-
А можно ли в OnExecute читать не определенное количество байт, а столько, сколько в буфере есть?
-
т.е в цикле перебираются все TClient из списка Clients (TList) на проверку логин/пароль
Вот это нахрена в OnExecute?
-
> Вот это нахрена в OnExecute?
а как быть?
-
Убрать цикл
-
> Убрать цикл
Я конечно извиняюсь, а как же тогда сделать идентификацию?
> Вот это нахрена в OnExecute?
А что тогда .. переводить всё в главный поток? тогда теряется многопоточность программы, если создавать отдельно поток, тогда теряется смысл блокирующего режима
-
Еще раз.
OnExecute - он выполняется для каждого подключения.
Кто-то подключился - у тебя онэкзекут.
Кто-то еще подключился - у тебя еще один онэкзеккут.
И каждый конкретный онэкзекут - он для конкретного подключения, конкретного клиента.
Еще раз вопрос: Нахрена что-то там перебирать среди всех клиентов, если метод вызыван для конкретного, "вот этого" клиента?
-
> Возможно ли в них обращение к списку Clients (TList) без
> синхронизации?
Невозможно если список может быть изменен в контексте любого иного потока.
-
> Еще раз вопрос: Нахрена что-то там перебирать среди всех
> клиентов, если метод вызыван для конкретного, "вот этого"
> клиента?
Или я в танке или вы меня не до конца понимаете.
естественно после идентификации при последующих обращенениях когда AContext.Data будет присвоен Указатель на TClient мне уже не нужна идентификация.
А если пользователь только что присоединился и посылает логин и пароль, я же должен проверить логин и пароль, а для этого нужен перебор, может вы знаете другой метод идентификации.
-
То есть ты хочешь сказать, что у тебя учетные записи живут в листе в виде экземпляров TClient?
И видимо лист грузится при старте?
Тогда конечно, перебор нужен. Но естественно защищенный критической секцией.
-
> RGV © (08.09.11 00:54) [10]
Только зачем это делать перебором?
-
> То есть ты хочешь сказать, что у тебя учетные записи живут
> в листе в виде экземпляров TClient?
> И видимо лист грузится при старте?
да.
По другому не знаю как... Если подскажете буду весьма признателен.
> Anatoly Podgoretsky © (08.09.11 09:34) [12]
А как? До меня пока не доходит ((
Вобчем насколько я понял onExecute любое обращение (и чтение и запись) к объектам к которым могут иметь доступ одновременно несколько потоков надо осуществлять через Synchronize. Вот так TidThread(AContext.Yarn).Synchronize(SomeProcedure) ?
если необходимо послать другому клинту/клиентам сообщение надо
как то так
tcp.Contexts.LockList
TidThread(AContext.Yarn).Synchronize(отправлять данные клиенту/клентам)
Synchronize - потому что будет обращение к TClient в процедуре отправки
tcp.Contexts.UnLockList.
Я правильно понимаю?
-
если необходимо послать другому клинту/клиентам сообщение надо ....
классическая ошибка.
Если необходимо послать клиенту сообщение, надо начинать программировать не с сервера, а с клиента.
Это клиент должен начинать белыми и запрашивать "нет ли для меня чего?"
А сервер должен ему ответить есть или нет.
не наоборот.
Иначе может получиться так:
Клиент что-то отправил серверу и ждет ответ на посылку.
Сервер в это самое время решил что -то наотправлять клиентам.
И клиент вместо ответа на одну конкретную команду получает от сервера ответ, никак не связанный с отправленным запросом.
Причем клиент в полной уверенности, что сервер-то ему ответил на его запрос.
-
> Медвежонок Пятачок © (08.09.11 10:51) [14]
Хм.... а как же тогда решить такую не хитрую задачу как отправка клиентом сообщения для других клиентов?
По вашему выходит так:
1 клиент A посылает клиенту Б сообщение
2 сервер принял и куда то там сохранил, либо у TCLient делать какой то буфер сообщений и туда сохранять все сообщения.
3 клиент (наверно по таймеру) спрашивает сервер "А нет ли для меня каких -нибудь сообщений) и тут сервер выдает ему весь буфер
так?
-
Это называется "придумать адекватный протокол обмена"
Без продуманного протокола у тебя ничего не получится.
-
Для начала можно попробовать свыкнуться с мыслью, что сервер - это обслуживатель запросов клиента.
Нет клиентского запроса - сервер курит.
Появился запрос - сервер его обслужил.
И курит дальше.
У сервера нет никакой инициативы.
-
Мда..... заставили вы меня репу почесать )
И все же как бы вы решили такую задачу
> RGV © (08.09.11 10:59) [15]
-
Мда..... заставили вы меня репу почесать )
И все же как бы вы решили такую задачу
> RGV © (08.09.11 10:59) [15]
-
> У сервера нет никакой инициативы.
т.е. сервер должен посылать только тому, чей запрос он в данный момент обслуживает и никому другому?
Неужели у клиентской программы добавлять таймер и по таймеру узнавать для него сообщения (от клиента, от сервера например об отключении какого то клиента и т.д), а на сервере для каждого клиента делать буфер сообений ?
-
> Медвежонок Пятачок © (08.09.11 11:08) [17]
Иначе это не сервер, а спамер.
-
> RGV © (08.09.11 11:19) [20]
Надо сменить идеологию, надо что бы клиент стал сервером, а сервер клиентом и всего делов. И никаких таймеров
-
> Anatoly Podgoretsky © (08.09.11 11:31) [22]
Издеваетесь?
А можно по подробней про идеологию?
Вот бы на исходничек взглянуть "правильного" клиента, и "правильного" сервера, разумеется на Indy.
-
> Anatoly Podgoretsky © (08.09.11 11:31) [22]
Я понимаю, что ломать парадигму задача не тривиальная, все ж таки попробуйте мне объяснить эту вашу идеологию.
-
> RGV (08.09.2011 11:41:23) [23]
Считай сам как хочешь, желание что либо отвечать совсем пропало.
-
> Anatoly Podgoretsky © (08.09.11 12:03) [25]
Я прошу прощения, если чем вас обидел.
-
> Для начала можно попробовать свыкнуться с мыслью, что сервер
> - это обслуживатель запросов клиента.
>
> Нет клиентского запроса - сервер курит.
Не всегда так. В ряде случаев долбать сервер постоянным опросом со стороны клиентов - накладно слишком, поэтому сервер может уведомлять клиентов сам по собственной инициативе о событиях. Стандартная практика.
Например, видел такое в интерфейсе Asterisk, в Mailru тоже вроде.
> Клиент что-то отправил серверу и ждет ответ на посылку.
> Сервер в это самое время решил что -то наотправлять клиентам.
>
>
> И клиент вместо ответа на одну конкретную команду получает
> от сервера ответ, никак не связанный с отправленным запросом.
>
Каждая посылка серверу должна снабжаться неким уникальным идентификатором, сервер в своих ответах тоже должен приводить тот же идентификатор. Тогда будет ясно какой ответ на какой запрос был.
Необходимость в последовательной обработке команд отпадает.
Такой асинхронный протокол обмена тоже часто используется. Для разного рода чатов подходит идеально.
-
Т.е в такой асинхронной схеме существует 3 типа посылок:
1. Команды клиента. Требуют ответа сервера.
2. Ответы сервера. Высылаются в ответ на команды клиента.
3. События сервера. Высылаются клиенту. Ответ на них не требуется.
-
Да, и Indy для описанного мной не очень подходит. Нужно что-то асинхронное и неблокирующее в идеале. Событийная модель используется.
-
> DVM © (08.09.11 23:25) [29]
Спасибо.
TServerSocket, TClientSocket для этих целей может подойти?
synapse?
-
> RGV © (09.09.11 04:23) [30]
ICS лучше всего
-
Не всегда так. В ряде случаев долбать сервер постоянным опросом со стороны клиентов - накладно слишком,
Чем накладно-то?
У него постоянный коннект клиента и сервер держит его сессию все время пока клиент не выйдет.
Вся накладность - в количестве байтов гоняемых по сети.
-
> Медвежонок Пятачок © (09.09.11 10:47) [32]
> Чем накладно-то?
> Вся накладность - в количестве байтов гоняемых по сети.
Сам же ответил. Данные будут гоняться, даже если нет для клиента никаких сообщений. Если клиентов мало - это ерунда конечно, но представь если таким образом сервер будет обслуживать 1000 клиентов. Значительная часть процессорного времени будет уходить только на ответы клиентам что данных нет (причем в большинстве случаев бестолковые). Туда же будет уходить и трафик (иногда критично, например, в случае GPRS). Зачем это все? Не проще ли переломить себя и наделить небольшой инициативой сервер?
Да, это несколько отличается от поведения классического сервера, но этам модель классического сервера пришла из юникс среды, где все в основном синхронное и блокирующее, так там проще было и прижилось и стало догмой.
-
Собственно не зря же придумано множество расширений к стандартным серверам для поддержки так называемых push технологий, когда данные высылаются клиентам по мере их появления, а не по запросам. Типичный пример почта на смартфонах Blackberry. Смартфон всегда на связи с сервером, но постоянных запросов не делает ибо накладно - трафик, сервер сам уведомляет клиента когда данные пришли.
-
но представь если таким образом сервер будет обслуживать 1000 клиентов.
Прикинь, не могу.
В этом кокретном случае.
Я даже не могу представить, что на предприятии автора когда-то внедрят эту подпорку (если он таки сделает первую альфу)
Так что ни о каких тысячах речь не идет.
Речь идет об обмене с его младшей сестрой.
Пять минут после написания проги.
Затем сестра снова уйдет во вконтакт со словами "не приставай ко мне со своей ерундой".
Но так как чел "хочет научиться" - то здесь как раз тот самый случай когда сервер не должен ничего делать кроме обслуживания запросов.
-
> Медвежонок Пятачок © (09.09.11 11:18) [35]
> Но так как чел "хочет научиться" - то здесь как раз тот
> самый случай когда сервер не должен ничего делать кроме
> обслуживания запросов.
В данном конкретном случае - это чат. Их делают в 99% случаях так как я описал, достаточно посмотреть на протоколы известных чатов. Почему бы ему не пойти по тому же пути (очевидно более рациональному изначально для этого применения). Чат - это взаимодействие с человеком в основном, поэтому тут событийная модель самое то.
В другой ситуации возможно надо придерживаться схемы запрос-ответ.
-
> DVM (09.09.2011 10:56:33) [33]
Да и постоянное подключение тоже в некоторых случаях накладно, когда
почасовое подключение.
-
> DVM (09.09.2011 11:01:34) [34]
Так делается это методом замены ролей, клиент становится сервером и
наоборот, но автор это не воспринимает.
-
> Anatoly Podgoretsky © (09.09.11 11:58) [38]
Да. Действительно не понимаю как это должно выглядеть не то чтобы на практике, но как это выглядит хотябы схематично.
В моем понимании сервер - это некая программа которая в ожидании входящих соединений, клиент - это программа которая "подключается" к серверу, и весь обмен информацией между клиентами идет через сервер.
-
> RGV © (10.09.11 03:46) [39]
Может не совсем грамотно выразился, но думаю смысл понятен.
> DVM © (09.09.11 10:38) [31]
Пытаюсь разобраться в работе TWSocketServer из ICS
там есть такое свойство MultiThreaded, она включает многопоточность? Т.е все события у TWSocketClient вызываются из их собственного потока? и скорее всего эти события вызываются без Synchroninize.
Спасибо за вашу помощь.
-
> [29] DVM © (08.09.11 23:25)
очень даже подходит, для клиентов уж точно. для сервера тоже подойдет, если клиентов не тысячи. а касаемо асинхронности - просто нужно четко разделить работу с сетью и остальную логику работы программы, в т.ч. UI. соединение должно быть в отдельном потоке и работать с очередью сообщений, посылаемых из UI и остальной части программы.
-
> Eraser © (10.09.11 16:06) [41]
> очень даже подходит
Выделять цельный поток для каждого из, скажем, 1000 клиентов, большинство из которых 99.99% времени ничего не передают - расточительно.
> RGV © (10.09.11 06:41) [40]
Там же вроде примеры есть? Многопоточность тебе не нужна.
-
> [42] DVM © (10.09.11 16:29)
если сервер расчитан на тысчи клиентов, то там ни Indy, ни ICS не подойдет. А для клиентов очень даже подходит.
-
> Eraser ©
> если сервер расчитан на тысчи клиентов, то там ни Indy,
> ни ICS не подойдет.
ICS несколько тысяч потянет. Используя порты завершения ввода-вывода можно и десятки тысяч обслужить (не знаю, использует ли ICS IOCP). Потоков более чем ядер смысла создавать в такой ситуации нет.
> А для клиентов очень даже подходит.
>
>
Да подойти то подойдет, но самый примитивный вариант асинхронных сокетов на сообщениях для клиента чата проще в реализации и синхронизация не нужна с интерфейсом.
-
> Eraser (10.09.2011 19:46:43) [43]
ICS нормально справляется с десятками тысяч клиентов
-
> [44] DVM © (10.09.11 20:21)
> [45] Anatoly Podgoretsky © (10.09.11 21:15)
обычно просто середины не бывает. либо сервер обслуживает несколько десятков клиентов и не больше, либо сервер должен потянуть и десятки тысяч. в этом случае да - только порты завершения. В ICS увы не используются. Вот если бы Indy или ICS написали хотя бы простенький сервер на базе IOCP - цены бы им не было. Есть неплохой компонент, автором которого является наш форумщик, но к компоненту есть ряд вопросов.
-
> Eraser © (10.09.11 22:42) [46]
> Есть неплохой компонент, автором которого является наш форумщик,
> но к компоненту есть ряд вопросов.
Этот?
THPServerSocket
Author: Sergey N. Naberegnyh
-
этот.
-
Спасибо за советы и помощь.