RPC无法解码TCP传输的参数 [英] RPC can't decode arguments for TCP transport

查看:154
本文介绍了RPC无法解码TCP传输的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在根据此页面上的示例在多线程RPC服务器上工作: http://bderzhavets.blogspot.ca/2005 /11/multithreaded-rpc-server-in-white-box.html

I'm working on a multithreaded RPC server based on the example from this page: http://bderzhavets.blogspot.ca/2005/11/multithreaded-rpc-server-in-white-box.html

不幸的是,它并没有开箱即用,在追寻错误一段时间后,我发现服务器无法解码参数(基于squareproc_2的返回码).在调用函数serv_request中的squareproc_2_svc之后,服务器端的执行似乎停止了.请参见来自square_svc.c的以下代码中的case: SQUAREPROC

Unfortunately it didn't quite work out of the box, and after chasing the errors for some time, I've found that the server is failing to decode the arguments (based on the return code from squareproc_2). The execution on the server side seems to stop after the call to squareproc_2_svc in the function serv_request. See case: SQUAREPROC in the code below from 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;
    }
}

这是来自square_server.c文件的squareproc_2_svc的实现:

Here is the implementation of squareproc_2_svc from the file 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在服务器端返回FALSE结果. 这是Square.x

As you can see, xdr_square_in returns a FALSE result on the server side. Here is the square.x

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

and 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中工作,使用rpcgen -a -M生成存根和xdr代码,并使用gcc进行编译.

I'm working in Ubuntu 14.04 LTS, generating stubs and xdr code with rpcgen -a -M, and compiling with gcc.

仅在将TCP用作传输方法时才会出现该错误.我可以使用UDP作为传输来获得结果,但是当多个客户端的请求同时到达时,某些调用将失败.我希望能够支持多达15个客户.当我尝试使用UDP和10个客户端时,10个调用中有2个失败,返回码与squareproc_2不同.

The error only seems to occur when using TCP as the transport method. I can get results using UDP as the transport, but some calls fail when requests from multiple clients arrive simultaneously. I would like to be able to support up to 15 clients. When I tried using UDP and 10 clients, 2 of the 10 calls failed with a different return code from squareproc_2.

推荐答案

您遇到了一些问题.

在xen页面上,当它在square_prog_2中执行pthread_create时,首先会调用pthread_attr_setdetachstate,但需要先进行pthread_attr_init .另外,attr似乎是静态的/全局的-将其放在函数的堆栈框架中.

From the xen page, when it does the pthread_create in square_prog_2, it first calls pthread_attr_setdetachstate, but it needs to do pthread_attr_init before that. Also, attr appears to be static/global--put it in the function's stack frame.

square_prog_2得到两个参数:rqstp和transp.这些被保存到malloc的struct data_str中[因此每个线程都有自己的副本].但是,我想知道rqstp和transp的值是什么(例如printf(%p")).它们需要不同,否则每个线程在尝试使用它们时将相互冲突(因此需要pthread_mutex_lock). malloc不会克隆rqstp/transp,所以如果它们相同,那就是问题所在,因为您可能有两个线程试图同时对同一缓冲区进行riff.

square_prog_2 gets two args: rqstp and transp. These get saved into a malloc'ed struct data_str [so each thread has their own copy]. But, I wonder what the rqstp and transp values are (e.g. printf("%p")). They need to different or each thread will collide with each other when trying to use them [thus needing pthread_mutex_lock]. The malloc doesn't clone rqstp/transp so if they are the same, that's the issue because you may have two threads trying to riff on the same buffers simultaneously.

返回码为11.除非有一些特殊的代码,否则看起来像线程中的SIGSEGV.这将由rqstp/transp重叠完全解决.

There is a return code of 11. Barring some special code, that looks suspiciously like SIGSEGV on a thread. This would be completely accounted for by the rqstp/transp overlap.

您可能需要重新构造此代码,因为我怀疑XDR不是 线程安全的-也不必这样做.另外,我不认为svc_ *是线程安全/可识别的.

You may need to rearchitect this as I suspect XDR is not thread safe--nor should it need to be. Also, I don't think svc_* is thread safe/aware.

启动单线程.作为测试,请square_prog_2直接调用serv_request(例如,执行pthread_ *).我敢打赌,这在所有模式下都有效.

Start single threaded. As a test, have square_prog_2 call serv_request directly (e.g. do not do pthread_*). I bet that works in all modes.

如果是这样,请戴上帽子-使用线程的示例代码已损坏-充满了竞争条件,并且可能会出现段错误等.如果您不愿意使用线程(无需执行此类轻型任务)如x * x),您可以按原样享受.

If so, hold onto your hat--the example code using threads is broken--full of race conditions and will segfault, etc. If you're not hung up on using threads (no need for such a light duty task as x * x), you can just enjoy as is.

否则,解决方案会更加复杂.主线程必须完成对套接字的所有访问以及所有XDR解析/编码.它不能使用svc_run-您必须自己滚动.小孩只能 做实际的工作(例如x * x),并且可能不能触摸插座/请求/传输等.

Otherwise, the solution is a bit more sophisticated. The main thread must do all the access to the socket and all XDR parsing/encoding. It can't use svc_run--you have to roll your own. The child can only do the actual work (e.g. x * x) and may not touch the socket/req/transp, etc.

主线程:

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
        }
    }
}

对于子结构,它看起来像:

For the child struct it would look like:

struct child_struct {
    int num;
    int num_squared;
};

孩子的线程函数变成一个衬里:ptr->num_squared = ptr->num * ptr->num

And the child's thread function becomes a one liner:ptr->num_squared = ptr->num * ptr->num

更新: Linux或FreeBSD上不支持的多线程RPC服务器

UPDATE: Multithread RPC servers appear to not be supported under Linux or FreeBSD

这是一个文档: https://www.redhat. com/archives/redhat-list/2004-June/msg00439.html 这是一个更干净的示例.

Here's a document: https://www.redhat.com/archives/redhat-list/2004-June/msg00439.html This has a cleaner example to start from.

据此:记住-Linux不支持rpcgen的选项.图书馆电话 在Linux下,SunOS RPC提供的用于构建多线程RPC服务器的功能也不可用

From that: Remember -A option of rpcgen is not supported under Linux. Library calls providing by SunOS RPC to build Multithreaded RPC Server are unavailable under Linux as well

这是Linux rpcgen手册页: http://linux.die.net/man/1 /rpcgen 没有提及-M. IMO,这意味着rpcgen程序可以选择并确实生成存根,但是底层支持不存在,因此他们将其排除在文档之外.

Here's the Linux rpcgen man page: http://linux.die.net/man/1/rpcgen No mention of -M. IMO, this means the rpcgen program has the option and does generate the stubs, but the underlying support is not there, so they left it out of the doc.

这是FreeBSD手册页[以及不支持的原因]:

Here's the FreeBSD man page [and the reason why there's no support]: http://www.freebsd.org/cgi/man.cgi?query=rpcgen&sektion=1&manpath=FreeBSD+5.0-RELEASE See the doc for -M within this:

M-生成用于传递参数和结果的多线程安全存根 在rpcgen生成的代码和用户编写的代码之间.这个选项 对于想要在代码中使用线程的用户很有用.但是,rpc_svc_calls(3)函数还不是MT安全的,因此 表示rpcgen生成的服务器端代码将不是MT安全的.

M -- Generate multithread-safe stubs for passing arguments and results between rpcgen generated code and user written code. This option is useful for users who want to use threads in their code. However, the rpc_svc_calls(3) functions are not yet MT-safe, which means that rpcgen generated server-side code will not be MT-safe.

另一种方法:

为什么还要打扰RPC/XDR?对于打算使用的大型阵列,开销非常大.大多数标准用途都是用于黄页之类的东西,这些数据没有太多.

Why bother with RPC/XDR at all? The overhead is huge for the large arrays you intend to use. Most of the standard uses are for things like yellow pages, that don't have much data.

如今,大多数系统都是低端的.只需将本机缓冲区加载到您直接打开的套接字即可.在服务器上,让守护程序进行侦听,然后 fork 让孩子进行接受,让孩子进行接受,读入数据,进行计算并发回答复.在最坏的情况下,孩子将需要进行字节序交换,但是使用bswap_32可以在紧密循环中轻松完成该操作.

Most systems are little endian these days. Just blast the native buffer to a socket that you open directly. On the server, have a daemon do a listen, then fork a child and have the child do the accept, read in the data, do the calculations, and send back the reply. At worst, the child will need to do an endian swap but that's easily done in a tight loop using bswap_32.

在每条消息的开头,在任一方向上都添加了一个简单的小控制结构,该结构以数据有效载荷为前缀:

A simple little control struct at the beginning of each message in either direction that prefixes the data payload:

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

特别说明:我之前已经做过商业性的操作(例如,MPI,然后自己动手做),您可能必须发出setsockopt调用,以将 kernel 套接字缓冲区的大小增加到足够大的程度维持大量数据

A special note: I've done this commercially before (e.g. MPI and roll my own) and you may have to issue setsockopt calls to increase the size of the kernel socket buffer to something large enough to sustain a barrage of data

实际上,现在我想到了,如果您不想自己动手,那么MPI可能会引起您的兴趣.但是,使用它后,我不是真正的粉丝.它有意想不到的问题,我们必须删除它,以便直接控制我们的套接字.

Actually, now that I think of it, if you don't want to roll your own, MPI may be of interest. However, having used it, I'm not a true fan. It had unexpected problems and we had to remove it in favor of controlling our sockets directly.

这篇关于RPC无法解码TCP传输的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆