Поделиться через


Разработка сервера с использованием дескрипторов контекста

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

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

После того как серверная программа направит дескриптор контекста на данные контекста, дескриптор считается открытым. Дескрипторы, содержащие значение NULL , закрываются. Сервер поддерживает открытый дескриптор контекста, пока клиент не вызовет удаленную процедуру, которая закрывает его. Если сеанс клиента завершается, когда дескриптор открыт, время выполнения RPC вызывает подпрограмму запуска сервера, чтобы освободить дескриптор.

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

/* cxhndlp.c (fragment of file containing remote procedures) */
typedef struct 
{
     FILE* hFile;
     char   achFile[256];
} FILE_CONTEXT_TYPE;

Функция RemoteOpen открывает файл на сервере:

short RemoteOpen(
    PPCONTEXT_HANDLE_TYPE pphContext,
    unsigned char *pszFileName)
{
    FILE               *hFile;
    FILE_CONTEXT_TYPE  *pFileContext;
 
    if ((hFile = fopen(pszFileName, "r")) == NULL) 
    {
        *pphContext = (PCONTEXT_HANDLE_TYPE) NULL;
        return(-1);
    }
    else 
    {
        pFileContext = (FILE_CONTEXT_TYPE *) 
                       MIDL_user_allocate(sizeof(FILE_CONTEXT_TYPE));
        pFileContext->hFile = hFile;
        // check if pszFileName is longer than 256 and if yes, return
        // an error
        strcpy_s(pFileContext->achFile, srlen(pszFileName), pszFileName);
        *pphContext = (PCONTEXT_HANDLE_TYPE) pFileContext;
        return(0);
    }
}

Функция RemoteRead считывает файл на сервере.

short RemoteRead(
    PCONTEXT_HANDLE_TYPE phContext, 
    unsigned char *pbBuf, 
    short *pcbBuf) 
{ 
    FILE_CONTEXT_TYPE *pFileContext; 
    printf("in RemoteRead\n"); 
    pFileContext = (FILE_CONTEXT_TYPE *) phContext; 
    *pcbBuf = (short) fread(pbBuf, sizeof(char), 
                            BUFSIZE, 
                            pFileContext->hFile); 
    return(*pcbBuf); 
}

Функция RemoteClose закрывает файл на сервере. Обратите внимание, что серверное приложение присвоило дескриптору контекста значение NULL в рамках функции close. Это сообщает заглушку сервера и библиотеке времени выполнения RPC о том, что дескриптор контекста был удален. В противном случае подключение будет оставаться открытым, и в конечном итоге произойдет запуск контекста.

void RemoteClose(PPCONTEXT_HANDLE_TYPE pphContext)
{
    FILE_CONTEXT_TYPE *pFileContext;
 
    if (*pphContext == NULL)
    {
        //Log error, client tried to close a NULL handle.
        return;
    }
    pFileContext = (FILE_CONTEXT_TYPE *)*pphContext;
    printf("File %s closed.\n", pFileContext->achFile);
    fclose(pFileConext->hFile);
    MIDL_user_free(pFileContext);
 
    // This tells the run-time, when it is marshalling the out 
    // parameters, that the context handle has been closed normally.
    *pphContext = NULL;
}

Примечание

Хотя ожидается, что клиент передает допустимый дескриптор контекста вызову с атрибутами направления [in, out], RPC не отклоняет маркеры контекста NULL для этой комбинации атрибутов направления. Дескриптор контекста NULL передается серверу в виде указателя NULL . Код сервера для вызовов, содержащих дескриптор контекста [in, out], должен быть написан, чтобы избежать нарушения доступа при получении указателя NULL .