The Russian Electronic Developer Magazine | |
Русский электронный журнал разработчика | |
Итак, в предыдущем материале мы рассмотрели, как организовать в рамках ICS (ICAPI) собственную обработку запросов к WWW-серверу. Теперь, чтобы перейти к созданию приложений клиент-сервер, нам необходимо решить вопрос - каков будет протокол обмена между сервером и клиентом? Из этого будет сделан вывод: как сервер и клиент будут отправлять и принимать данные. Я для себя выбрал следующее решение: запросы на сервер посылаются стандартным для броузера способом, но чтобы отличать их от обычных запросов, перед ними ставиться знак '?'. Например - "GET http://www.myserver.ru/MyApplication?GET_VERSION=1". Похожим образом организована работа с запросами во многих продуктах (Lotus Domino, MS Active Pages). Ответ от сервера поступает клиенту в стандартном виде для WWW-сервера, со всеми заголовками и т.д. (см. http://ru2.halfos.ru/rdm2/articles/http.html). Можно было,конечно, устроить чтение/запись черем tcp/ip socekts в виде двоичных данных, но описанный подход, на мой взгляд, удобнее по нескольким причинам:
Что же касается набора ключевых слов протокола, то это решать разработчику конретного продукта. От себя скажу, что в качестве упражнения пробовал создать интерфейс доступа к DBF-файлам, для чего использовал в качестве ключевых слов имена функций библиотеки CodeBase (библиотека работы с DBF на С и C++), и их параметры. Получается этакий протокол RPC (Remote Procedure Call).
Я использовал разбор запроса на шаге PreExit. Вот так примерно может выглядеть обработчик для этого шага :
CODE4 DBFsession; // это структуры данных нужные для DATA4 *pDBF; // CODEBASE. char URL [] = "URL"; // переменная содержащая // значение текущего URL char QUERY_STRING [] = "QUERY_STRING"; // переменная содержащая запрос void HTTPD_LINKAGE Init ( unsigned char *handle, unsigned long *major_version, unsigned long *minor_version, long *rcode ) { d4init(&DBFsession); // инициализируем интерфейс CODEBASE *rcode = HTTP_OK; } void HTTPD_LINKAGE PreExit ( unsigned char *handle, long *rcode ) { unsigned char *URL = NULL; unsigned char *querySTRING = NULL; unsigned char *p; unsigned long l; unsigned char Buf [80]; *rcode = HTTP_NOACTION; // код - "ничего не делали" if ( (URL=GetCGIvar(handle,URL)) != NULL ) { // выделяем URL strupr ( URL ); // смотрим, было обращение к нам ? if ( (p=strstr(URL,"CODEBASE")) != NULL && *(p+8) == '?' ) { // было, выделим запрос в чистом виде querySTRING = GetCGIvar ( handle, QUERY_STRING ); if ( querySTRING != NULL ) { // если выделили найдем в нем if ( (p=strstr(querySTRING,"D4OPEN=")) != NULL ) { // команду и ее аргумент p += 8; // выполним действие pDBF = d4open(&DBFsession,p); // выводим заголовок http HTTP_WriteHeader(handle,rcode); if ( pDBF != NULL ) { // выводим текст в зависимости sprintf ( Buf, "D4OPEN=0,%lx\n", pDBF ); } // от результата else { // обработка ошибок пропущена } l = strlen (Buf); HTTPD_write ( handle, Buf, &l, rcode ); *rcode = HTTP_OK; } } } } if ( URL != NULL ) // освободим память free ( URL ); if ( querySTRING != NULL ) free ( querySTRING ); }Теперь, чтобы выполнить то или иное действие, достаточно в строке URL-броузера задать : http://www.myserver.ru/CODEBASE?d4open=myfile.dbf. На экране броузера в качестве ответа будет выдан текст :
d4open=0,223443где, первый параметр - код завершения операции открытия файла, а второй handle-файла или, если была ошибка, то ответ будет выглядеть :
d4open=5,0где, первый параметр - код ошибки, а handle будет 0.
Я думаю, дальше пояснять не надо.
Замечание 1. Нельзя забывать, что все параметры функций, полученные от клиента, на сервере должны быть проверены на допустимость, иначе рискуете "вальнуть" весь сервер.
Замечание 2. Это только модель, для реального использования придется позаботится о блокировках в DBF-файлах и о создании для каждого клиента своей структуры данных (списка клиентов, соденинений)
Замечание 3. Приведенный только участок кода, полный пример можно найти здесь.
Замечание 4. Если вы будете применять CODEBASE в таком режиме, необходимо использовать вариант его библиотеки для DLL и помнить о размере стека.
При работе с Интернет, клиент может "отвалиться", либо самостоятельно прервать соединение. Возникает проблема - что делать с "беспризорными" клиентами (соединениями)? Я поступил следующим образом : в структуру данных, управляющей соединением с клиентом, добавлено поле "время последнего запроса". При инициализации сервера (шаг ServerInit) запускается низкоприоритетный thread, который с заданным интервалом сканирует список соединений, определяет тех, у которых истек таймаут и удаляет их.
Теперь поговорим о клиенте. Для себя я выбрал Java. Я не буду касаться особенностей языка. Для обращения использую стандартный класс java.net.URLConnection. Например, то же обращение в Java-исполнении будет выглядеть :
public String d4open ( String BaseURL, String DBFfile ) throws java.lang.Exception { URLConnection conn = null; InputStream fp = null; byte bArray []; URL pURL = null; String openURL = null; int contentlength; String openresult = null; openURL = BaseURL + "/CODEBASE?D4OPEN=" + DBFfile; pURL = new URL(openURL); // строим строку запроса из базового URL // сервера и имени файла conn = pURL.openConnection(); // готовим соединение fp = conn.getInputStream(); // работа с URL в Java идет также как и с // файлами, получим handle на файл conn.connect(); // выполняем запроса (на самом деле, понять // в какой момент происходит передача данных // в Java достаточно сложноб thread-s обнако if ( (contentlength = conn.getContentLength()) > 0 ) { // смотрим, что получили в ответ ? bArray = new byte [contentlength]; fp.read(bArray);// читаем входной поток как массив байт fp.close(); // закрываем соединение openresult = new String(bArray,0,bArray.length); // преобразуем ответ в строку, которую } // и вернем pURL = null; conn = null; return openresult; }И в заключение. Мне представляется, что данная технология предоставляет чрезвычайно мощный и одновременно простой способ создания распределенных Интернет-приложений. С ее использованием мы сейчас создаем систему поддержки дистанционного образования (рабочее название ЕКОС).
Андрей ПородькоПрограммирование ICAPI для ICS. Часть 1
Интересные ссылки:
Комментариев к странице: 0 | Добавить комментарий
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов