RPC не может декодировать аргументы для транспорта TCP

Я работаю над многопоточным сервером RPC на основе примера с этой страницы: http://bderzhavets.blogspot.ca/2005/11/multithreaded-rpc-server-in-white-box.html

К сожалению, это не совсем работало из коробки, и после некоторой погони за ошибками я обнаружил, что серверу не удается декодировать аргументы (на основе кода возврата из squareproc_2). Казалось, выполнение на стороне сервера прекращается после вызова squareproc_2_svc в функции serv_request, Увидеть case: SQUAREPROC в приведенном ниже коде из square_svc.c

void *serv_request(void *data)
{
    struct thr_data *ptr_data = (struct thr_data *)data;
    {
        square_in argument;
        square_out result;
        bool_t retval;
        xdrproc_t _xdr_argument, _xdr_result;
        bool_t (*local)(char *, void *, struct svc_req *);
        struct svc_req *rqstp = ptr_data->rqstp;
        register SVCXPRT *transp = ptr_data->transp;
        switch (rqstp->rq_proc) {
            case NULLPROC:
                printf("NULLPROC called\n");
                (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
                return;
            case SQUAREPROC:
                _xdr_argument = (xdrproc_t) xdr_square_in;
                _xdr_result = (xdrproc_t) xdr_square_out;
                printf("_xdr_result = %ld\n",_xdr_result);
                local = (bool_t (*) (char *, void *,  struct svc_req *))squareproc_2_svc;
                break;
            default:
                printf("default case executed");
                svcerr_noproc (transp);
                return;
        }
        memset ((void *)&argument, 0, sizeof (argument));
        if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
            printf("svc_getargs failed");
            svcerr_decode (transp);
            return;
        }
        retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
        printf("serv_request result: %d\n",retval);
        if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result))
        {
            printf("something happened...\n");
            svcerr_systemerr (transp);
        }
        if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
            fprintf (stderr, "%s", "unable to free arguments");
            exit (1);
        }
        if (!square_prog_2_freeresult (transp, _xdr_result, (caddr_t) &result))
            fprintf (stderr, "%s", "unable to free results");
        return;
    }
}

Вот реализация squareproc_2_svc из файла square_server.c:

bool_t squareproc_2_svc(square_in *inp,square_out *outp,struct svc_req *rqstp)
{
    printf("Thread id = '%ld' started, arg = %ld\n",pthread_self(),inp->arg1);
    sleep(5);
    outp->res1=inp->arg1*inp->arg1;
    printf("Thread id = '%ld' is done %ld \n",pthread_self(),outp->res1);
    return(TRUE);
}

Вывод на стороне клиента:

yak@AcerPC:~/RPC/multithread_example$ ./ClientSQUARE localhost 2
squareproc_2 called
xdr_square_in result: 1
function call failed; code: 11

Вывод на стороне сервера:

yak@AcerPC:~/RPC/multithread_example$ sudo ./ServerSQUARE 
creating threads
SQUAREPROC called
xdr_square_in result: 0

Как видите, xdr_square_in возвращает ЛОЖНЫЙ результат на стороне сервера. Вот квадрат.х

struct square_in {
    long arg1;
};

struct square_out {
    long res1;
};

program SQUARE_PROG {
    version SQUARE_VERS {
        square_out SQUAREPROC(square_in) = 1;
    } = 2 ;
} = 0x31230000;

и square_xdr.c

/*
 * Please do not edit this file.
 * It was generated using rpcgen.
 */

#include "square.h"

bool_t
xdr_square_in (XDR *xdrs, square_in *objp)
{
    register int32_t *buf;
    int retval;
    if (!xdr_long (xdrs, &objp->arg1)) retval = FALSE;
    else retval = TRUE;
    printf("xdr_square_in result: %d\n",retval);
    return retval;
}

bool_t
xdr_square_out (XDR *xdrs, square_out *objp)
{
    register int32_t *buf;
    int retval;
    if (!xdr_long (xdrs, &objp->res1)) retval = FALSE;
    else retval = TRUE;
    printf("xdr_square_out result: %d\n",retval);
    return retval;
}

Я работаю в Ubuntu 14.04 LTS, создавая заглушки и код XDR с rpcgen -a -Mи компилировать с gcc,

Эта ошибка возникает только при использовании TCP в качестве метода транспорта. Я могу получить результаты, используя UDP в качестве транспорта, но некоторые вызовы не выполняются, когда запросы от нескольких клиентов поступают одновременно. Я хотел бы иметь возможность поддерживать до 15 клиентов. Когда я попытался использовать UDP и 10 клиентов, 2 из 10 вызовов потерпели неудачу с другим кодом возврата из squareproc_2,

1 ответ

Решение

У вас есть несколько вопросов.

Со страницы xen, когда он выполняет pthread_create в square_prog_2, он сначала вызывает pthread_attr_setdetachstate, но перед этим ему нужно выполнить pthread_attr_init. Кроме того, attr выглядит статическим / глобальным - поместите его в кадр стека функции.

square_prog_2 получает два аргумента: rqstp и transp. Они сохраняются в malloc'е struct data_str [поэтому каждый поток имеет свою собственную копию]. Но мне интересно, что такое значения rqstp и transp (например, printf("%p")). Они должны различаться, или каждый поток будет сталкиваться друг с другом при попытке их использовать [поэтому требуется pthread_mutex_lock]. Malloc не клонирует rqstp/transp, поэтому, если они одинаковые, это проблема, потому что у вас может быть два потока, пытающихся риффить на одни и те же буферы одновременно.

Существует код возврата 11. За исключением некоторого специального кода, который выглядит подозрительно как SIGSEGV в потоке. Это было бы полностью учтено перекрытием rqstp/transp.

Возможно, вам придется пересмотреть архитектуру, так как я подозреваю, что XDR не является поточно-ориентированным - и не должно быть. Кроме того, я не думаю, что svc_* потокобезопасен / осведомлен.

Пуск однопоточный. В качестве теста сделайте так, чтобы square_prog_2 напрямую вызывал serv_request (например, не делайте pthread_*). Могу поспорить, что работает во всех режимах.

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

В противном случае решение будет немного сложнее. Основной поток должен делать весь доступ к сокету и весь анализ / кодирование XDR. Он не может использовать svc_run - вы должны бросить свой собственный. Ребенок может выполнять только реальную работу (например, x * x) и не может касаться гнезда /req/transp и т. Д.

Основная тема:

while (1) {
    if (svc_getreq_poll()) {
        // parse XDR
        // create data/return struct for child thread
        // create thread
        // add struct to list of "in-flight" requests
    }

    forall struct in inflight {
        if (reqdone) {
            // take result from struct
            // encode into XDR
            // do send_reply
            // remove struct from list
        }
    }
}

Для дочерней структуры это будет выглядеть так:

struct child_struct {
    int num;
    int num_squared;
};

И функция дочернего потока становится однострочной: ptr->num_squared = ptr->num * ptr->num

ОБНОВЛЕНИЕ: Многопоточные RPC-серверы, по-видимому, не поддерживаются в Linux или FreeBSD

Вот документ: https://www.redhat.com/archives/redhat-list/2004-June/msg00439.html Это более чистый пример, с которого можно начать.

Из этого: Помните, что опция rpcgen не поддерживается в Linux. Вызовы библиотеки, предоставляемые SunOS RPC для создания многопоточного сервера RPC, также недоступны в Linux

Вот справочная страница Linux rpcgen: http://linux.die.net/man/1/rpcgen Нет упоминания о -M. IMO, это означает, что программа rpcgen имеет опцию и генерирует заглушки, но основной поддержки нет, поэтому они оставили ее вне документа.

Вот справочная страница FreeBSD [и причина, по которой нет поддержки]: http://www.freebsd.org/cgi/man.cgi?query=rpcgen&sektion=1&manpath=FreeBSD+5.0-RELEASE Смотрите документацию для -M в этом:

M - Создать многопоточные заглушки для передачи аргументов и результатов между кодом, сгенерированным rpcgen, и кодом, написанным пользователем. Эта опция полезна для пользователей, которые хотят использовать потоки в своем коде. Однако функции rpc_svc_calls(3) еще не являются MT-безопасными, что означает, что сгенерированный rpcgen код на стороне сервера не будет MT-безопасным.

Альтернативный способ:

Зачем вообще беспокоиться о RPC/XDR? Накладные расходы огромны для больших массивов, которые вы собираетесь использовать. Большинство стандартных применений для таких вещей, как желтые страницы, которые не имеют много данных.

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

Простая небольшая управляющая структура в начале каждого сообщения в любом направлении, которая ставит префикс полезных данных:

struct msgcontrol {
    int what_i_am;
    int operation_to_perform;
    int payload_length;
    int payload[0];
};

Специальное примечание: я делал это коммерчески раньше (например, MPI и свернул свой собственный), и вам, возможно, придется выполнить вызовы setsockopt, чтобы увеличить размер буфера сокета ядра до чего-то достаточно большого, чтобы выдержать кучу данных

На самом деле, теперь, когда я думаю об этом, если вы не хотите снимать свои собственные, MPI может представлять интерес. Однако, пользуясь им, я не настоящий фанат. У него были неожиданные проблемы, и нам пришлось убрать его в пользу прямого контроля наших сокетов.

Другие вопросы по тегам