RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

Web технологии. Программирование ICAPI для ICS. Часть 2

Еще раз оговорюсь, что не претендую на истину в последней инстанции, и оставляю за собой право ошибаться :-). И с удовольствием пообщаюсь лично с теми кто работает в этой области.

Итак, в предыдущем материале мы рассмотрели, как организовать в рамках 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 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов


(C) Russian Underground/2