-
RGV © (06.09.11 18:06) [0]Доброе время суток!
На работе потребовался 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
//здесь планируется разорвать соединение, незнаю как
//AContext.Connection.disconnect не разрывает связь.
end;
exit;
end;
end;
header:=BytesToHeader(Buffer);
if not ValidHeader(header,ErrMsg) then
begin
//в лог добавляем ошибку
//здесь планируется разорвать соединени
exit;
end;
if Acontext.Data=nil then//в AContext.data находится указатель на TClient если пользователь уже вошел.
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) на проверку логин/пароль
в случае, если логин/пароль неверны нужно послать сообщение пользователю и разорвать соединение.
Заранее благодарен. -
Медвежонок Пятачок © (06.09.11 20:13) [1]OnExecute обрабатывает конкретную нить конкретного подключения.
Зачем и почему здесь потребовалось
т.е в цикле перебираются все TClient из списка Clients (TList) на проверку логин/пароль ?
PS дунотдисторб написано с ошибкой.
PPS забей, займись чем-нибудь не столь бесполезным. -
RGV © (07.09.11 01:14) [2]
> OnExecute обрабатывает конкретную нить конкретного подключения.
Это я понимаю.
> Зачем и почему здесь потребовалось
Не понял вопроса! Что потребовалось? -
RGV © (07.09.11 07:49) [3]А можно ли в OnExecute читать не определенное количество байт, а столько, сколько в буфере есть?
-
Медвежонок Пятачок © (07.09.11 10:45) [4]т.е в цикле перебираются все TClient из списка Clients (TList) на проверку логин/пароль
Вот это нахрена в OnExecute? -
RGV © (07.09.11 12:24) [5]
> Вот это нахрена в OnExecute?
а как быть? -
Медвежонок Пятачок © (07.09.11 13:15) [6]Убрать цикл
-
RGV © (07.09.11 13:28) [7]
> Убрать цикл
Я конечно извиняюсь, а как же тогда сделать идентификацию?
> Вот это нахрена в OnExecute?
А что тогда .. переводить всё в главный поток? тогда теряется многопоточность программы, если создавать отдельно поток, тогда теряется смысл блокирующего режима -
Медвежонок Пятачок © (07.09.11 14:24) [8]Еще раз.
OnExecute - он выполняется для каждого подключения.
Кто-то подключился - у тебя онэкзекут.
Кто-то еще подключился - у тебя еще один онэкзеккут.
И каждый конкретный онэкзекут - он для конкретного подключения, конкретного клиента.
Еще раз вопрос: Нахрена что-то там перебирать среди всех клиентов, если метод вызыван для конкретного, "вот этого" клиента? -
Сергей М. © (07.09.11 16:58) [9]
> Возможно ли в них обращение к списку Clients (TList) без
> синхронизации?
Невозможно если список может быть изменен в контексте любого иного потока. -
RGV © (08.09.11 00:54) [10]
> Еще раз вопрос: Нахрена что-то там перебирать среди всех
> клиентов, если метод вызыван для конкретного, "вот этого"
> клиента?
Или я в танке или вы меня не до конца понимаете.
естественно после идентификации при последующих обращенениях когда AContext.Data будет присвоен Указатель на TClient мне уже не нужна идентификация.
А если пользователь только что присоединился и посылает логин и пароль, я же должен проверить логин и пароль, а для этого нужен перебор, может вы знаете другой метод идентификации. -
Медвежонок Пятачок © (08.09.11 08:59) [11]То есть ты хочешь сказать, что у тебя учетные записи живут в листе в виде экземпляров TClient?
И видимо лист грузится при старте?
Тогда конечно, перебор нужен. Но естественно защищенный критической секцией. -
Anatoly Podgoretsky © (08.09.11 09:34) [12]
> RGV © (08.09.11 00:54) [10]
Только зачем это делать перебором? -
RGV © (08.09.11 10:44) [13]
> То есть ты хочешь сказать, что у тебя учетные записи живут
> в листе в виде экземпляров 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]если необходимо послать другому клинту/клиентам сообщение надо ....
классическая ошибка.
Если необходимо послать клиенту сообщение, надо начинать программировать не с сервера, а с клиента.
Это клиент должен начинать белыми и запрашивать "нет ли для меня чего?"
А сервер должен ему ответить есть или нет.
не наоборот.
Иначе может получиться так:
Клиент что-то отправил серверу и ждет ответ на посылку.
Сервер в это самое время решил что -то наотправлять клиентам.
И клиент вместо ответа на одну конкретную команду получает от сервера ответ, никак не связанный с отправленным запросом.
Причем клиент в полной уверенности, что сервер-то ему ответил на его запрос. -
RGV © (08.09.11 10:59) [15]
> Медвежонок Пятачок © (08.09.11 10:51) [14]
Хм.... а как же тогда решить такую не хитрую задачу как отправка клиентом сообщения для других клиентов?
По вашему выходит так:
1 клиент A посылает клиенту Б сообщение
2 сервер принял и куда то там сохранил, либо у TCLient делать какой то буфер сообщений и туда сохранять все сообщения.
3 клиент (наверно по таймеру) спрашивает сервер "А нет ли для меня каких -нибудь сообщений) и тут сервер выдает ему весь буфер
так? -
Медвежонок Пятачок © (08.09.11 11:05) [16]Это называется "придумать адекватный протокол обмена"
Без продуманного протокола у тебя ничего не получится. -
Медвежонок Пятачок © (08.09.11 11:08) [17]Для начала можно попробовать свыкнуться с мыслью, что сервер - это обслуживатель запросов клиента.
Нет клиентского запроса - сервер курит.
Появился запрос - сервер его обслужил.
И курит дальше.
У сервера нет никакой инициативы. -
RGV © (08.09.11 11:11) [18]Мда..... заставили вы меня репу почесать )
И все же как бы вы решили такую задачу
> RGV © (08.09.11 10:59) [15] -
RGV © (08.09.11 11:12) [19]Мда..... заставили вы меня репу почесать )
И все же как бы вы решили такую задачу
> RGV © (08.09.11 10:59) [15]