Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Обмен данными между клиентом и сервером

 

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

В DDE различают три способа получения данных от сервера, называемых видами связи. Эти три вида связи называются холодная, теплая и горячая. Коротко поясним различия этих видов связи:

· холодная связь — cold link

обмен данными происходит только по запросу клиента. Сервер посылает в ответ данные или отрицательное подтверждение.

· горячая связь — hot link

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

· теплая связь — warm link

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

Последние два вида связи (теплая и горячая) называются иногда постоянной (permanent) связью.

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

Когда два приложения обмениваются данными друг с другом, они передают друг другу хендлы блоков данных и атомы. Для того, что бы эти данные были доступны обоим приложениям, они должны быть глобальными. Однако надо учесть, что обычно глобальные блоки данных связаны с тем приложением, которое их создало, то есть при завершении приложения эти данные автоматически уничтожаются. Так как процесс DDE–разговора является асинхронным, то необходимо обеспечивать сохранность данных независимо от существования создавшего их приложения. Для этого глобальные блоки данных должны быть разделяемыми — при их выделении надо указывать флаг GMEM_DDESHARE (или GMEM_SHARE, который является синонимом).

В случае платформы Win32 используются прежние функции для выделения глобальных блоков данных, несмотря на то, что блоки выделяются только в локальном для каждого процесса виртуальном адресном пространстве. Система автоматически осуществляет передачу данных из адресного пространства одного процесса в адресное пространство другого процесса при передаще соответствующих сообщений.

Особое внимание надо уделить вопросу освобождения ресурсов, так как атомы и передаваемые блоки данных сами не уничтожаются, даже если все участвующие в DDE приложения завершили работу. Все созданные объекты должны быть обязательно уничтожены, независимо от исхода операции. Сложности связаны с тем, что один и тот–же объект может быть уничтожен либо клиентом, либо сервером, в зависимости от протекания процесса обмена, а, кроме того, в процессе обмена могут присходить различные ошибки (например, функция PostMessage не может послать сообщение).

“Холодная” связь — cold link

 

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

WM_DDE_REQUEST                       hWnd  aItem & cfFormat

это сообщение является требованием передачи данных. Параметр wParam является хендлом пославшего сообщение окна, а lParam содержит в младшем слове номер формата данных (номера те–же, что используются буфером обмена), а в старшем - атом имени данных. Платформы Win32 и Windows 3.x используют это сообщение одинаково.

В ответ на это сообщение сервер может ответить либо сообщением WM_DDE_DATA, если он имеет необходимые данные и может их послать, либо “отрицательным” сообщением WM_DDE_ACK, указывающим, что сервер не может послать требуемые данные.

WM_DDE_DATA hWnd                   aItem & hData (Packed Win32)

Это сообщение передает данные клиенту. Младший компонент lParam содержит хендл глобального разделяемого блока данных hData. Этот параметр может быть равен 0, что реально встречается только в случае “теплой” связи (см. ниже). Передаваемый блок данных должен начинаться со структуры DDEDATA, флаг fAckReq которой указывает на необходимость отправки подтверждения о получении данных. Передача: сервер не может сбрасывать одновременно оба бита fRelease и fAckReq в 0. То есть корректными являются только три возможных комбинации fRelease и fAckReq, при которых можно определить, кто должен освобождать блок данных — клиент или сервер. Освобождение ресурсов: атом aItem должен удаляться, если только он не посылается с ответным сообщением WM_DDE_ACK (если бит fAckReq заголовка данных равен 1, то есть требуется ответ). Освобождение блока данных зависит от установки битов fRelease и fAckReq заголовка данных:

 

fRelease fAckReq  
0 1 блок данных всегда освобождается сервером при получении ответа от клиента (считается, что к моменту получения сервером подтверждения клиент уже прочитал требуемые данные и они ему больше не нужны).
1 0 блок данных всегда освобождается клиентом, сервер ответа не получает (так клиент может некоторое время сохранять полученные данные независимо от течения DDE–разговора).
1 1 при успешном чтении блок данных освобождается клиентом, а в случае ошибки (то есть при получении сервером отрицательного подтверждения) блок освобождается сервером. Этот вариант близок к предыдущему, за исключением того, что за ошибочно полученный блок клиент ответственности не несет.

 

Структура DDEDATA содержит следующие данные:

typedef struct tagDDEDATA {          WORD unused:12,                        fResponse:1,  // 1 в ответ на WM_DDE_REQUEST, 0 — WM_DDE_ADVISE        fRelease:1,      // данные должны быть удалены после получения   reserved:1,     fAckReq:1;              // 1 — необходимо послать подтверждение о приеме             short cfFormat;       // формат данных          BYTE Value[1];      // сами данные } DDEDATA;

Внимание! в документации по Windows 3.x SDK и Win32 SDK содержалась ошибка — там были перепутаны пояснения к полям fResponse и fAckReq, так что описание структуры DDEDATA противоречило описанию сообщения WM_DDE_DATA. В документации, сопровождающей компилятор Microsoft Visual C++ v4.0 эта ошибка была исправлена.

Заметьте, что сервер может потребовать подтверждения о приеме посланных данных. Для этого он устанавливает бит fAckReq в заголовке переданных данных равным 1. В таком случае необходимо послать ответное сообщение WM_DDE_ACK (положительное или отрицательное, если возникла какая-либо ошибка).

 

 

Если сервер не имеет нужных данных, то вместо WM_DDE_DATA клиент получит WM_DDE_ACK (отрицательное)

 

 

WM_DDE_ACK hWnd                   aItem & wStatus (Packed Win32)

Младший компонент lParam содержит информацию о посылаемом подтверждении. Младший байт этого слова содержит код, возвращаемый приложением, старший байт содержит два значащих бита 0x80 и 0x40. Часто этот параметр представляется в виде структуры DDEACK. Старшее слово lParam представляет собой атом, идентифицирующий данные. Согласно документации этот атом обязательно должен быть удален при обработке этого сообщения. В случае платформы Win32 параметр lParam используется для передачи “упакованного” значения; при этом надо помнить, что если WM_DDE_ACK приходит в ответ на WM_DDE_INITIATE, то параметр lParam содержит обычные, не упакованные данные.

Структура DDEACK содержит следующие данные:

typedef struct tagDDEACK {             WORD bAppReturnCode:8, // код, определяемый приложением           reserved:6,     fBusy:1, // 1 — сервер занят и не может обработать запрос       fAck:1; // 1 — сообщение было получено приложением } DDEACK;

В зависимости от значений битов fBusy и fAck различают следующие возможные виды ответов:

· fAck = 1 fBusy = 0 — положительное подтверждение

· fAck = 0 fBusy = 0 — отрицательное подтверждение

· fAck = 0 fBusy = 1 — сервер занят, либо не может обработать запрос в разумные сроки (что есть “разумные” сроки — не поясняется)

· Согласно документации установка обеих флагов fAck и fBusy одновременно должна быть исключена.

Вместо структуры DDEACK часто используют просто битовые шаблоны — 0x8000 соответствует положительному подтверждению, 0 — отрицательному и 0x4000 — сервер занят (вообще говоря, это тоже отрицательное подтверждение, хотя запрос можно попробовать повторить).

Дополнительно ситуация осложняется тем, что в процессе DDE–разговора применяется посылка, а не передача сообщений. То есть между посылкой сообщения серверу и получения от него ответа может пройти значительное время, в течении которого необходимо обрабатывать остальные поступающие сообщения, система при этом может переключаться на другие приложения и, что самое неприятное, в это время либо клиент, либо сервер могут вообще завершить работу. В этой ситуации приходится вставлять специальные фрагменты кода, осуществляющие ожидание того или иного сообщения в течении указанного промежутка времени и принимать специальные меры, если ответа слишком долго нет.

// // wait for time out // static BOOL TimeOut(HWND hWnd, UINT wMsg, UINT uTimeOut) {       DWORD dwTime;           MSG msg;

    // FALSE design normal termination          // TRUE design time-out or negative WM_DDE_ACK         dwTime= GetCurrentTime();             while (dwTime + uTimeOut > GetCurrentTime()) {                if (PeekMessage(&msg, hWnd, NULL, NULL, PM_REMOVE)) {                            // обработать полученное сообщение                               TranslateMessage(&msg);                    DispatchMessage(&msg);                           if (msg.message == WM_DDE_ACK) {                                     // check for.fAck                                     return LOWORD(msg.lParam) & 0x8000? FALSE: TRUE;                   } else if (msg.message == wMsg) return FALSE;                  }            }

    return TRUE; }

Обычно процедура, ожидающая нужное сообщение, выглядит несколько иначе. Это связано с желательным отображением курсора ожидания (в виде песочных часов), а также с обработкой сообщений, извлеченных из очереди. Во многих случаях нормальная обработка сообщений не сводится только к функциям TranslateMessage и DispatchMessage, но также включает поддержку акселераторов, немодальных диалогов и пр. Помимо этого следует учесть, что в случае платформы Windows 3.x размер очереди сообщений приложения сравнительно невелик — так что при извлечении сообщений, направленных только конкретному окну, очередь может легко переполниться.

Еще одно замечание касается времени ожидания ответа от сервера. Этот промежуток не надо назначать очень большим, но в то же время он должен быть достаточным, что бы даже на медленных машинах уверенно получать ответ от сервера. При избыточно большом времени ожидания ответа может возникать впечатление “зависания” приложения, пока оно ждет ответа. Обычно человек ожидает от компьютера почти мгновенной реакции или, в худшем случае, явно видимой работы. Если программа останавливается на срок более 10–15 секунд, то уже возникает беспокойство о ее работе.

Практически приемлемым является интервал порядка 5–30 секунд, так как на средней машине ответ может задерживаться до 5–10 секунд при нормальной работе и, соответственно, на медленных машинах интервал следует увеличить примерно до тридцати секунд. (Различие между быстрыми и медленными машинами весьма относительно — с ростом мощности компьютеров растет нагрузка на них, поэтому время выполнения более–менее значительных операций снижается сравнительно медленно). Возможным решением является двухэтапное ожидание — первый этап до 3–6 секунд проходит как обычно, а по истечении этого интервала отображается окно диалога с кнопкой “Cancel”, после чего ожидание продолжается либо до получения ответа, либо до отмены ожидания пользователем. Это позволяет пользователю оценить занятость системы и, при необходимости, продолжить ожидание в разумных пределах.

Задача определения времени ожидания ответа от сервера является достаточно важной из–за необходимости освобождать занятые ресурсы. Фактически, при обмене сообщениями возникает следующая ситуация: при посылке каких либо разделяемых объектов от одного приложения к другому (это могут быть атомы или глобальные блоки данных) необходимо эти объекты удалять после их использования. Однако тут надо учесть следующие нюансы:

· если функция PostMessage не может послать сообщение, объекты можно удалять сразу, так как второе приложение их не получит.

· если сообщение послано, то момент уничтожения объектов выбирается в соответствии с описанием сообщения в документации. Так, например, атомы обычно удаляются по следующей схеме

- при получении посланного запроса проверяется необходимость ответа, если ответ нужен, то атом передается вместе с ответом и уничтожается приложением, пославшим запрос

- если ответ нужен, но его послать не удается, то атом уничтожается получившим запрос приложением

- если ответ не нужен, то атом опять–же уничтожается получившим запрос приложением

· посланное сообщение может “не дойти” до адресата (например, если окно–получатель будет закрыто уже после того, как к нему в очередь попадет это сообщение, но до его обработки). При этом ответа не будет, а приложение–получатель освободить ресурсы не сможет.

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

В двух последних случаях вопрос освобождения ресурсов остается открытым, так как приложение, пославшее запрос, не может получить информацию о результатах его выполнения, и, соответственно, о необходимости освобождения данных. При этом можно слегка изменить рассмотренное ранее “двухэтапное” ожидание, добавив в диалог кнопку или флажок освобождения ресурсов. Обычно пользователь может оценить причину задержки ответа — сбой в работе, либо досрочное завершение работы приложения и момент возникновения этой ситуации — до обработки или после. По крайней мере, если ситуация будет повторяющейся, то пользователь сможет подобрать наиболее “дешевый” выход из нее.

“Горячая” связь — hot link

 

Протокол при горячей связи. После инициации обмена клиент посылает запрос WM_DDE_ADVISE, указывая желаемые данные. Сервер отвечает о возможности горячей связи, и, обычно, сразу же посылает первые данные, для инициации клиента. Таким образом осуществялется как–бы “подписка” на получение необходимых данных при их изменении. Этот вид связи удобен, если Вам необходимо взаимодействовать с какой–либо базой данных, содержимое которой может динамически обновляться, как например база данных “товары на складе” для больших магазинов — доступ к этой базе одновременно могут иметь разные отделы магазина и список товаров, имеющихся на сладе, постоянно изменяется. Использование горячей связи в этом случае позволит создать локальные списки на компьютерах разных отделов и автоматически обновлять их, как только осуществляется изменение общей базы данных.

 

 

WM_DDE_ADVISE                           hWnd aItem & hOptions (Packed Win32)

Младший компонент параметра lParam содержит хендл глобального разделяемого блока, содержащего небольшую структуру DDEADVISE, а старший компонент — атом, описывающий данные на которые осуществляется подписка. Структура DDEADVISE определяет тип связи — горячая или холодная, а также необходимость требовать подтверждения о доставке данных от клиента. В случае платформы Win32 параметр lParam используется для передачи “упакованного” значения. Сервер, получивший это сообщение обязан ответить на него посылкой подтверждения WM_DDE_ACK, положительного или отрицательного. Освобождение ресурсов: Клиент, пославший запрос, должен дождаться ответа от сервера. Если ответ положительный, то блок данных должен быть удален сервером, а если отрицательный, то клиент должен сам его удалить.

Структура DDEADVISE содержит следующие данные:

typedef struct {        WORD reserved:14,                          fDeferUpd:1,           // 1-”теплая” связь, 0-”горячая”                            fAckReq:1;                       // 1-необходимо требовать подтверждение, 0-не надо      short cfFormat;                // желаемый формат данных } DDEADVISE;

При задании бита fDeferUpd равным 1 осуществляется так называемая теплая связь, при которой сервер только информирует о наличии у него измененных данных, но не посылает их клиенту непосредственно. В случае горячей связи этот бит должет быть равен 0. Бит fAckReq, равный 1, указывает на необходимость требовать от клиента подтверждения о получении данных. Позже, когда сервер начнет посылать клиенту сообщения WM_DDE_DATA он установит в структуре DDEDATA бит fAckReq соответствующим тому, который Вы указали в структуре DDEACK и, соответственно, клиент должен будет подтверждать получение им данных от сервера (надо учесть, что бит fAckReq влияет на стратегию освобождения посылаемых данных — подробнее см. описание сообщения WM_DDE_DATA).

Поле cfFormat указывает на формат данных, которые клиент ожидает от сервера. Вы можете для одного вида данных в пределах одного DDE–разговора подписаться на получение данных разного типа одновременно, посылая WM_DDE_ADVISE столько раз, сколько форматов данных Вы хотите получать. Однако такая возможность разрешена только для горячей связи. Согласно документации подписка на одни и те–же данные в разных форматах для теплой связи запрещена.

Сервер, при получении сообщения WM_DDE_ADVISE, проверяет возможность осуществить подписку на эти данные, создает необходимые ему структуры данных и отвечает клиенту сообщением WM_DDE_ACK — положительным или отрицательным. Это сообщение в данном случае используется так–же, как и при ответе на WM_DDE_DATA.

Если подписка осуществлена успешно, то далее сервер начинает извещать клиента об обновлении данных, осуществляя каждый раз пересылку этих данных клиенту с помощью уже рассмотренного сообщения WM_DDE_DATA. Единственное отличие от получения данных в ответ на WM_DDE_REQUEST заключается в том, что бит fResponse структуры DDEDATA равен 0, тогда как при отправке данных по запросу он устанавливается в 1. Это позволяет определить причину получения данных при обработке сообщения.

 

 

Заканчивается такой обмен посылкой WM_DDE_UNADVISE серверу. В этом сообщении указывается, от подписки на какие данные клиент отказывается (так как теоретически один клиент может подписаться сразу на несколько видов данных).

 

 

WM_DDE_UNADVISE                     hWnd  aItem & cfFormat

Младшее слово параметра lParam описывает формат данных, а старшее слово — атом, описывающий данные на которые осуществляется подписка. В случае платформы Win32 параметр lParam используется также, как и в случае Windows 3.x. Если параметр cfFormat равен 0, то отменяется подписка на указанные данные для всех тех форматов, для которых была осуществлена подписка. Если параметр aItem равен NULL, то отменяется подписка на все виды данных в пределах данного DDE–разговора. Сервер, обработав это сообщение, должен послать в ответ подтверждение WM_DDE_ACK, положительное или отрицательное; при этом если одна операция отменяет подписку на данные в разных форматах, то посылается только одно подтверждение.

Сообщение WM_DDE_UNADVISE не эквивалентено WM_DDE_TERMINATE, после его обработки отменяется только подписка, а сам DDE–разговор продолжается, так что клиент может установить затем новый вид связи с сервером. Сервер обязан ответить на сообщение WM_DDE_ADVISE положительным или отрицательным подтверждением.

“Теплая” связь — warm link

 

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

Как и горячая связь, теплая устанавливается с помощью сообщения WM_DDE_ADVISE с единственным исключением — бит fDeferUpd структуры DDEADVISE равен 1, а не 0.

 

 

Далее сервер начинает информировать клиента об обновлении (или наличии) у него нужных данных. Для этого используется обычное сообщение WM_DDE_DATA, которое в данном случае не передает никаких данных. Для этого параметр hData (младшее слово lParam или, в случае Win32, младшая компонента, извлекаемая с помощью UnpackDDElParam) устанавливается равным 0. Клиент, получая такое сообщение без данных, может немедленно запросить нужные данные или просто запомнить, что данные изменились и позже, при необходимости, получить их.

 

 

Заметьте, что в случае горячей связи вы можете подписаться сразу на получение информации об изменении одних и тех–же данных в разных форматах, а в случае теплой связи это запрещено. Такой запрет объясняется тем, что при поддержке таких видов связи для разных данных или одних и тех–же данных, но в разных форматах, клиент должен вести список данных и форматов на которые осуществлена подписка. При получении WM_DDE_DATA, информирующего об изменении данных имя данных задается параметром aItem (упакован в lParam), а формат данных — полем cfFormat структуры DDEDATA. Однако в случае теплой связи сообщение WM_DDE_DATA не содержит данных и, следовательно, информация о формате данных недоступна, при этом однозначно найти запись в спике не представляется возможным, если только не наложить ограничение — при теплой связи подписываться можно на данные только в одном формате, тогда запись списка будет однознано определена именем данных.

Для того, что бы получить нужные данные, клиент просто посылает отдельный запрос WM_DDE_REQUEST, как в случае холодной связи. Таким образом теплая связь является “гибридом” двух других видов связи — холодной и горячей.

 

 

Отмена подписки в случае теплой связи ничем не отличается от такого–же действия для горячей связи — клиент посылает сообщение WM_DDE_UNADVISE серверу, который отвечает на это подтверждением.

 

 

Работа с DDE в случае теплой или горячей связи обычно не вызывает значительных сложностей; однако надо обратить внимание на возможность использования нескольких видов связи одновременно. Обычно, при написании приложений используются предположения типа “В ответ на посланное WM_DDE_REQUEST должно прийти либо WM_DDE_DATA, либо WM_DDE_ACK”. Однако в жизни ситуация может оказаться существенно сложнее — если одно окно использует несколько соединений, то в ответ на посланный WM_DDE_REQUEST сначала может прийти сообщение WM_DDE_DATA, информирующее о получении данных (в случае теплой или горячей связи) и только потом настоящий ответ на WM_DDE_REQUEST. Для того, что бы уменьшить вероятность таких ситуаций, для каждого DDE–разговора создают специальное скрытое окно, взаимодействующее с сервером (или клиентом). Однако при разработке собственных приложений такую возможность все–равно надо учитывать, так как в процедурах обработки сообщений придется проверять, в связи с каким событием получено то или иное сообщение; а при разработке DDE–клиентов надо постараться разделить разные виды обмена данными либо по разным DDE–разговорам, либо по времени.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...