Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Как лучше реализовывать архитектуру клиент-сервер [D7, WinXP]


Страницы:   1 2 3

bobah ©   (2006-07-12 16:43:00) 

Здравствуйте.

Начну с описания. Существует у меня три вида программ. 1 - БД , 2 - сервер опроса приборов (клиент БД), 3 - клиентское ПО (ПО с которым взаимодействует пользователь). Это не трехзвенная модель в полном понимании.  Программа 3 допустим вставляет запись в БД, при этом обращается непосредственно в БД. В программе 3 есть журнал событий, котором отображаются изменения БД. Для того чтобы сообщить всем клиентам об изменениях прога 3 взаимодействует с 2 с помощью сокетов. То есть по сокетам передаются сообщения типа refresh. Клиент посылает серверу опроса сообщение, а сервер опроса рассылает это же сообщение всем остальным клиентам.
Теперь конкретнее. Я использую WinSock 2.2 асинхронную модель на событиях. С клиентом всё ясно, а вот с сервером не всё. Важно чтобы сообщения, приходящие на сервер, уходили на другие клиенты в том порядке, в котором они приходят на сервер. Как это обеспечить?
На данный момент серверная часть работает в одном отдельном потоке. Причем события read от всех клиентов обрабатываются в одной функции WSAWaitForMultipleEvents.


Evgeny V   (2006-07-18 16:00:00) [1]

Вот как раз и у нас есть датчик темпиратуры. Event -  ну можно формировать аварийные например Event на сокетах или еще каким способом, хотя например в IB/FB или оракл есть соотвествующий механизм. Про MSDE2000 сказать не могу - не знаю. Но так как тепло штука инерционная и датчик возможно не пожарный, а просто мониторинг например состояния погоды или климатики в серверной например, то и без events  можно обойтись на мой взгляд, а работать на обновление данных чисто по таймеру из БД.
Задача в этом случае (в случае без сокетов) значительно упрощается на мой взгляд. Хотя конечно условия и постановка вашей задачи вам видней
:-)  
Cервер опроса приборов - и у нас он примерно так же зовется, программа опроса приборов выполнена в виде сервиса(ов).


bobah   (2006-07-18 15:35:00) [2]

> Evgeny V

При разработке данной системы, я так и предполагал, что все взаимодействия будут осуществлятся через СУБД, без всяких сокетов. При этом я очень много всего пересмотрел, передумал:
вплоть до того что сменил БД с MySQL4 -> MySQL5 -> MSDE2000. Да, у меня есть журнал событий с указанием времени всех изменений, по которму можно обновлять данные в клиентах, причем только необхадимую часть.
Потом я загорелся сокетами, даже уже не помню почему...

В данный момент все эти механизмы в средней части написания: сокеты, БД, опрос приборов через модем и т.д.
Поэтому сейчас возвращаться, так сказать, к началу не хочеться.

Поэтому окончательно я пришел к следующей схеме:
1) клиенты и сервер непосредственно взаимодействуют с базой данных, причем обе стороны могут её модифицировать.
2)Основное назначение сокетов это, так сказать, общение между клиентом и прибором в "реальном времени", причем эти данные не обязательно сохраняются в базе данных.Иниаторм общения может быть как сервер, так и клиент.
Побочное назначение сокетов (раз уж они есть) это передача ивэнтов с сервера на клиенты сигналящие, что пора обновить данные с БД. Причем ивэнты формируются НЕ от клиентов, а от БД.
3)сервер - это не сервер приложений, а сервер опроса приборов.

> опаздание в плюс несколько секунд допустимо?

Не кретично. Тепло, штука инерционная.


Evgeny V   (2006-07-18 14:03:00) [3]

Почитал - думается как быстро реагирует пользователь на изменения показаний приборов не критично, опаздание в плюс несколько секунд допустимо ?  Если да, рекомендую модель предложенную Сергей М. А именно подумать о возможности использования клиент-серверной CУБД.  Сокеты в явном виде в этом случае можно не использовать (не нужны просто).
В итоге имеем СУБД, сервис(ы) получающие данные от приборов и записывающие их в БД с указанием даты и времени измерения, клиентское приложение, которое по таймеру
(можно и по событиям-алертам субд, если субд поддерживает такой механизм, но лучше по таймеру на мой взгляд, почему -это отдельная тема)
получает обновления с БД, причем в том порядке сортировки, который вам наиболее удобен, т.е. проблема
"
>  приходящие на сервер, уходили на другие клиенты в том порядке,
>
>   в котором они приходят на сервер

" отпадает, клиент сам выбирает данные в нужном ему порядке.


bobah   (2006-07-18 13:58:00) [4]

Я ОЧЕНЬ люблю функции типа WaitForMultipleObject. В них можно event от других потоков получать, а для моих реализаций это очень удобно.


bobah   (2006-07-18 13:56:00) [5]

> Зачем самоделку делаешь Чем TServerSocket не устроил?

Поздно, я уже сделал:). Плюс ко всему мне так понятнее.


Slym   (2006-07-18 12:08:00) [6]

Зачем самоделку делаешь Чем TServerSocket не устроил?


Медведъ   (2006-07-18 11:17:00) [7]

>bobah ©   (18.07.06 10:55) [40]
ничем не отличается, ступил, сорри


bobah   (2006-07-18 10:55:00) [8]

Тогда там должно быть наверное ; пропущена и end [37].

И чем тогда [37] отличается от моего кода приведенного в [32]?

Извиняюсь, если чего-то не вижу, но я не вижу.


Медведъ   (2006-07-18 10:42:00) [9]

>bobah ©   (18.07.06 10:35) [38]
[37] посмотри еще раз, и сравни с [38]
в моем случае если WOULDBLOCK ничего не происходит, ф-ия просто возвращает управление, когда придет ФД_ВРАЙТ она будет вызвана вновь и попытается отправить данные из очереди


bobah   (2006-07-18 10:35:00) [10]

> WSAEWOULDBLOCK не будет... сокет у тебя и так блокирующий...

Сокет у меня не блокирующий, функция WSAEventSelect, вызывается раньше чем в Excute;

> UDP бродкаст не пробовал?

Думал над этим, но сервер имеет режим при котором он должен посылать текущие данные с приборов опроса, причем клиент должен менять некоторые параметры, которые посылаются на сервер. Не знаю пока... UDP ведь не гарантирует доставку сообщений, да и порядок не соблюдает, наверное не подойдет.

> в реальной сети все будет сложнее

Попробуем в реальной сетке.

Насчет кода:

То есть должно быть так:


// Запись пакета
SendBytes := Send(ClientSocket,TxData.Buff[0],TxData.Len,0);
if SendBytes <> SOCKET_ERROR then
begin
  TxData.Len := TxData.Len - SendBytes;
  SetLength(TxData.Buff,TxData.Len);
end else
begin
  if WSAGetLastError <> WSAEWOULDBLOCK then
  begin
    CloseClientSocket;
    Result := False;
  end else
  begin
    TxData.Len := TxData.Len - SendBytes;
    SetLength(TxData.Buff,TxData.Len);
  end;
end;
end;


В этом случае, если пытаться сократить буффер при ошибке WSAEWOULDBLOCK


TxData.Len := TxData.Len - SendBytes;
SetLength(TxData.Buff,TxData.Len);


То буффер, наборот, станет больше, т.к. SendBytes = SOCKET_ERROR = -1
Судя по справке на Send() в случа WSAEWOULDBLOCK надо пробовать отправлять данные позже


Медведъ   (2006-07-18 08:51:00) [11]

>делал на localhost, какой бы буффер не делал ( 200000 байт) все равно всё отправлялось.
в реальной сети все будет сложнее

>Я привел в последнем сообщении код отправки SEND(), похоже?
нет

if SendBytes=SOCKET_ERROR then
begin
 if WSAGetLastError<><>WSAEWOULDBLOCK then
 begin
    CloseClientSocket;
    Result := False;
 end
else
begin
  TxData.Len := TxData.Len - SendBytes;
  SetLength(TxData.Buff,TxData.Len);
end


Slym   (2006-07-18 06:11:00) [12]

UDP бродкаст не пробовал?


Slym   (2006-07-18 06:08:00) [13]

WSAEWOULDBLOCK не будет... сокет у тебя и так блокирующий...


bobah   (2006-07-17 17:10:00) [14]

> а что у мсде2000 нет ограничений на локальные подключения а на удаленные есть ?

Нет, просто думал сделать одно дополнительное соединение на сервере, а на клиентах не делать, просто по сетке сообщать им о том, что пора обновляться. тогда у меня будет три клиента.

> этого делать не нужно

Просто, хотел эту ситуацию воспроизвести и посмотреть как ведет себя программа, делал на localhost, какой бы буффер не делал ( 200000 байт) все равно всё отправлялось.

>   при необходимости отправки данных нужно их добавить в очередь и после этого вызывать процедуру Ч которая будет пытаться отправлять данные из очереди
при событии ФД_ВРАЙТ просто вызываем Ч


Я привел в последнем сообщении код отправки SEND(), похоже?


Медвед   (2006-07-17 14:56:00) [15]

>Возник вопрос, как справоцировать программу к ошибке WSAEWOULDBLOCK при вызове SEND()?
этого делать не нужно
при необходимости отправки данных нужно их добавить в очередь и после этого вызывать процедуру Ч которая будет пытаться отправлять данные из очереди
при событии ФД_ВРАЙТ просто вызываем Ч

>Только, хочу сделать второе соединение на стороне сервера, т.к. использую MSDE 2000.
а что у мсде2000 нет ограничений на локальные подключения а на удаленные есть ?


bobah   (2006-07-17 13:31:00) [16]

> Для сей цели тебе был рекомендован scktcomp.pas.

За выходные просмотрел.

Пришел к следующим выводам: каждое клиентское соединение оформил в отдельный поток, независящий друг от друга; сделал кэш свободных потоков; от идеи с пересылкой обновлений от одного клиента всем клиентам отказался.

Возник вопрос, как справоцировать программу к ошибке WSAEWOULDBLOCK при вызове SEND()? Пока реализовано так:


procedure TSocketClientThread.Execute;
var ClientEvents: Array[0..2] of TWSAEvent;
   Sync,SendBytes: Cardinal;
   NetworkEvents: TWSANetworkEvents;
begin
 if FOpened then
 begin
   ClientEvents[0]:=Self.CloseEvent;
   ClientEvents[1]:=ClientSockEvent;
   ClientEvents[2]:=SuspendEvent;
   
   while not Terminated do
   begin
     sync := WSAWaitForMultipleEvents(3, @ClientEvents, false, INFINITE, false);
     case sync of
       WSA_WAIT_EVENT_0  : break;   // close event
       WSA_WAIT_EVENT_0+1:          // ClientSockEvent
       begin
         WSAEnumNetworkEvents(ClientSocket,ClientEvents[1],@NetworkEvents);

         if NetworkEvents.lNetworkEvents and FD_WRITE>0 then
         if NetworkEvents.iErrorCode[FD_WRITE_BIT]=0 then
         begin
           // Обработка пакета
           // TODO: Проверить на правильность отправки данных
           if TxData.Len <> 0 then
           begin
             SendBytes := Send(ClientSocket,TxData.Buff[0],TxData.Len,0);
             if SendBytes <> SOCKET_ERROR then
             begin
               TxData.Len := TxData.Len - SendBytes;
               SetLength(TxData.Buff,TxData.Len);
             end else
             begin
               if WSAGetLastError <> WSAEWOULDBLOCK then
               begin
                 CloseClientSocket;
               end;
             end;
           end;
         end else
         begin
           // Error := 'Ошибка отправки данных';
           CloseClientSocket;
         end;
<---- Обрыв ---->

function TSocketClientThread.ProcRecvMsg: Boolean;
var SendBytes: Cardinal;
begin
 Result := True;
 if RxData.Buff[1] = $01 then
 begin
   SetLength(TxData.Buff,TxData.Len+3);
   TxData.Buff[TxData.Len] := $03;
   TxData.Buff[TxData.Len+1] := $01;
   TxData.Buff[TxData.Len+2] := Byte(SetUserID(RxData.Buff[2])); // 1-ok
   TxData.Len := TxData.Len+3;
 end;

 // Запись пакета
 SendBytes := Send(ClientSocket,TxData.Buff[0],TxData.Len,0);
 if SendBytes <> SOCKET_ERROR then
 begin
   TxData.Len := TxData.Len - SendBytes;
   SetLength(TxData.Buff,TxData.Len);
 end else
 begin
   if WSAGetLastError <> WSAEWOULDBLOCK then
   begin
     CloseClientSocket;
     Result := False;
   end;
 end;
end;


RxData.Buff,TxDataBuff: Array of Byte;

Для решения проблемы с своевременным обновлением данных решил копать в направлении:

> Медвед

Только, хочу сделать второе соединение на стороне сервера, т.к. использую MSDE 2000.


Медвед   (2006-07-14 16:50:00) [17]

на скл.ру была статья на тему сабжа
суть ее в том, что клиент открывает дополнительное соединение с сервером бд в отдельном потоке и вызвает в нем некую процедуру
эта процедура ждет пока засигналит определенный именованый объект синхронизации - например Event
когда необходимо послать уведомления, в триггере или SP переводим объект в сигнальное состояние и все клиенты вызвавшие процедуру получают ответ
процедуры ожидания ивента и установки его в сигнальное состояние оформляются в виде ext stored procedures


Сергей М.   (2006-07-14 16:04:00) [18]


> Это позволит мне разобраться в самой сути работы это технологии
> на практике


Для сей цели тебе был рекомендован scktcomp.pas.
Судя по вопросам, ты даже не пошевелился в этом направлении.
А зря.


bobah   (2006-07-14 15:43:00) [19]

> Источники же твоей склонности именно к WSAPI тлобой не конкретизированы

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

А за помощь спасибо.


Сергей М.   (2006-07-14 15:33:00) [20]

Если у тебя ЛВС под управлением MSWindows, сокеты тебе нафих не нужны - для этой сети есь родные транспортныет технологии/механизмы.
Засим вопрос отпадает сам по себе.

А даже если те самые сокеты и нужны, то для этого существуют готовыекомпоненты, реализующие TCP/IP-транспорт. Все твои вопросы по этому транспорту моментально отпадают при использовании, например, Indy. Источники же твоей склонности именно к WSAPI тлобой не конкретизированы, поэтому дальнейший разговор лишен смысла.


Страницы:   1 2 3

Наверх

Титульная страница Поиск, карта сайта Написать письмо