Конференция "Сети" » Сервер на TIdTCPServer из Indy10 [D7, WinXP]
 
  • 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]
 
Конференция "Сети" » Сервер на TIdTCPServer из Indy10 [D7, WinXP]
Есть новые Нет новых   [134435   +15][b:0][p:0.002]