![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как лучше реализовывать архитектуру клиент-сервер [D7, WinXP]
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 тлобой не конкретизированы, поэтому дальнейший разговор лишен смысла.