Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.12.26;
Скачать: CL | DM;

Вниз

Клиент - сервер проблема синхронизации   Найти похожие ветки 

 
QuestionX ©   (2004-10-09 15:26) [0]

Возникла следующая ситуация: есть сервер, отвечающий за авторизацию пользователей в системе и обеспечивающий посылку клиентам сообщений от системы. Перед началом своей работы, клиент устанавливает соединение с сервером и при окончании разрывает его. Пока клиент посылает серверу запросы, а тот на них отвечает проблем нет, а вот когда сервер тоже может послать запрос клиенту, возникает следующая проблема - как избежать ситуации, когда клиент и сервер одновременно посылают запросы друг другу и оба переключаются в режим обработки этих запросов ?


 
Verg ©   (2004-10-09 16:22) [1]


>  когда клиент и сервер одновременно посылают запросы друг
> другу и оба переключаются в режим обработки этих запросов


И что? в чем проблема?


> Перед началом своей работы, клиент устанавливает соединение
> с сервером и при окончании разрывает его.


При окончании... при окончании чего?

Похоже, нехватает форума www.logicmaster.ru


 
QuestionX ©   (2004-10-09 18:04) [2]


> При окончании... при окончании чего?

Перед началом своей работы, клиент устанавливает соединение с сервером и при окончании [своей работы] разрывает его.

> И что? в чем проблема?

Цикл работы сервера:
1: Ожидать получения команды от пользователя или системного события
  Если получение команды то 2 иначе 3

2: Считать команду
  <Ожидать получения command specific данных
  Считать данные
  Ответить клиенту>
  Перейти к 1

3: Послать команду клиенту
  Ожидать получения данных ответа
  Считать данные
  Перейти к 1


Цикл работы клиента:
1: Ожидать получения команды от сервера или запроса от пользователя
  Если получение команды то 2 иначе 3

2: Считать команду
  <Ожидать получения command specific данных
  Считать данные
  Ответить серверу>
  Перейти к 1

3: Послать команду серверу
  Ожидать получения данных ответа
  Считать данные
  Перейти к 1


Так вот, если предположить одновременное возникновение запроса пользователя на клиенте и системного события на сервере, то
1: сервер переключится в режим Ожидать получения command specific данных (N байт) и получит вместо них команду клиента (4 байта)

2: клиент будет Ожидать получения данных ответа (N байт) а получит вместо них команду от сервера (4 байта)

Так они и остануться висеть в ожидании несбыточного, пока тайм-аут не разлучит их навеки.
Вполне возможно, что мне действительно нехватает www.logicmaster.ru но пока я не вижу как иначе решить проблему обмена по одному каналу, не создавая еще одного соединения.


 
Verg ©   (2004-10-09 18:38) [3]

Так все это делается на уровне логики протокола информационного обмена. Каждая сторона ожидает не "то ответа, то запроса", а "ответа или запроса", т.е. готова либо принять ответ, либо запрос в любой момент, в который она вообще чего-либо готова принимать от собеседника. Порции информации должны быть оформлены в одинаково понятном для обеих сторон виде. Например пакетов. Пакет сотоит из заголовка фиксированной длины и тела произвольной длины. Однако длина тела всегда указывается в заголовке пакета. В заголовке так же указывается служебная информация. Например, что это за пакет - запрос или ответ. Если ответ, то указывается на каой запрос. Идентификация ответов идет по уникальному значению в заголовке запроса. По ИД-у. Пример ида - просто cardinal который запрашивающий каждый раз увеличивает на единицу и помещает в поле заголовка запроса. Отвечающий на конкретно этот вопрос помещает ИД того запроса в заголовок паета с ответом на этот запрос....

И т.д. и т.п. и множество существует способов организации взаимодействия....


 
QuestionX ©   (2004-10-09 19:10) [4]


> Каждая сторона ожидает не "то ответа, то запроса", а "ответа
> или запроса", т.е. готова либо принять ответ, либо запрос
> в любой момент

Это то, что я долго и безуспешно пытаюсь сделать. Не могли бы подсказать алгоритм реализации подобного поведения сервера или хотя бы ссылочку где об этом можно почитать.


 
QuestionX ©   (2004-10-12 00:34) [5]

Как ни крути, не могу понять одной вещи. Пусть сервер и клиент могут обмениваться сообщениями, для простоты пусть

THeader = packed record
 Command: Integer;
 BodySize: Integer;
end;
TEvent = (evHeaderRead, evNotify);

Тогда на сервере для каждого подключившегося клиента работает следующий код:

var
 Header: THeader;
 Event: TEvent;
...

while Connected do
 Event := ReadBuffer(Header, sizeof(Buffer)); // Отсюда возврат в 2-х случаях - прочитан присланный клиентом заголовок или сработал объект синхронизации, уведомляющий о системном событии
 case Event of
 evHeaderRead: // Считали заголовок пакета
 begin
   ReadBuffer(Buffer, Header.Size); // Считываем тело пакета
   ... // Обработка
   Header.Command := ...
   Header.Size := ...
   SendBuffer(Header, sizeof(Header)); // Посылаем заголовок ответного пакета
   SendBuffer(Buffer, BufSize); // Посылаем тело пакета
 end;
 evNotify: // Произошло некоторое событие
 begin
   ... // Обработка
   Header.Command := ...
   Header.Size := ...
(*)    SendBuffer(Header, sizeof(Header)); // Посылаем заголовок ответа
   SendBuffer(Buffer, BufSize); // Посылаем тело ответ
   ReadBuffer(Header, sizeof(Buffer)); // Считываем заголовок ответа
   ReadBuffer(Buffer, Header.Size); // Считываем тело ответа
 end;
 end;
end;


На клиенте цикл полностью аналогичен.

И возникает ситуация о которой я говорил выше - коллизия, когда системые события возникают и на сервере и на клиенте и строка (*) срабатывает на обеих сторонах.
Как этого избежать ? Как реализовать "Каждая сторона ожидает не "то ответа, то запроса", а "ответа или запроса", т.е. готова либо принять ответ, либо запрос в любой момент"  подскажите алгоритм, пожалуйста или ссылку на то где это подробно описано.

С уважением.


 
Verg ©   (2004-10-12 06:23) [6]

Если рассуждать в твоих же терминах, то, например:
TCommand = (cmdREQUEST, cmdRESMONSE);

THeader = packed record
Command: Integer; // TCommand
Id     : Cardinal;
BodySize: Integer;
end;
TEvent = (evHeaderRead, evNotify);

var
 CurrentXID : cardinal = 1;
 WaitingRespXid : cardinal = 0;

begin
   while Connected do
   begin
    Event := ReadBuffer(Header, sizeof(Buffer)); // Отсюда возврат в 2-х случаях - прочитан присланный клиентом заголовок или сработал объект синхронизации, уведомляющий о системном событии
    case Event of
      evHeaderRead: // Считали заголовок пакета
      begin
        ReadBuffer(Buffer, Header.Size); // Считываем тело пакета
        case TCommand(Header.Command) of
          cmdREQUEST : begin
                     ... // Обработка
                     Header.Command := ...
                     Header.Size := ...
                     // Заметь - мы оставили поле ID в ответе нетронутым
                     // т.е. ID ответа равен ID-у запроса
                     SendBuffer(Header, sizeof(Header)); // Посылаем заголовок ответного пакета
                     SendBuffer(Buffer, BufSize); // Посылаем тело пакета
          end;
          cmdRESPONSE: begin
            // Принят
             if( WaitingRespXID = Header.Id ) then
             // или определяем нет ли в очереди наших запросов этого Id-а
             begin
             // Принят ответ на недавно отправленный запрос
               WaitingRespXID := 0;
               // Обработка принятого ответа.............
             end;
          end;
        end;
      end;
      evNotify: // Произошло некоторое событие
      begin
        if WaitingRespXID <> 0 then
        begin
          // Мы еще не получили ответа на предыдущий свой запрос
          // поэтому можно либо игнорировать системыное событие
          // либо организовать очередь (напр., Tlist) из своих запросов
          // Тогда WaitingRespXID - это не просто переменная, а некий объект
          // - очередь запросов, которые уже отправлены, но ответы на них
          // не получены
        end;
        ... // Обработка
        repeat
          Inc(CurrentXID);
        until CurrentXID <> 0;
        Header.Command := ord(cmdREQUEST);
        Header.Id      := CurrentXID;
        WaitingRespXID := Header.Id; // либо вносим этот запрос в очередь
                                     // ожидающих ответ
        Header.Size := ...
        SendBuffer(Header, sizeof(Header)); // Посылаем заголовок ответа
        SendBuffer(Buffer, BufSize); // Посылаем тело ответ
      end;
    end;
end;
end;


 
QuestionX ©   (2004-10-12 16:39) [7]


> Verg ©   (12.10.04 06:23) [6]

Спасибо !


 
Verg ©   (2004-10-12 22:21) [8]

Надеюсь, ты понимаешь, что это не код, а лишь принцип, т.е. схема...



Страницы: 1 вся ветка

Текущий архив: 2004.12.26;
Скачать: CL | DM;

Наверх




Память: 0.51 MB
Время: 0.024 c
6-1097076789
Константин Эдуардович
2004-10-06 19:33
2004.12.26
Сокетный движок.


14-1102265524
Pat
2004-12-05 19:52
2004.12.26
Прикольная игрушка для автолюбителей


14-1102311556
Чеширский Кот из Гондураса
2004-12-06 08:39
2004.12.26
Позвольте представиться


1-1102685710
mefisto
2004-12-10 16:35
2004.12.26
Как захватить текущий URL из окна Internet Explorer.


14-1102260093
Супермэн
2004-12-05 18:21
2004.12.26
Двойная мораль или отсутствие какой-либо морали?