RPC无法解码TCP传输的参数

我正在研究基于此页面示例的multithreadingRPC服务器: http : //bderzhavets.blogspot.ca/2005/11/multithreaded-rpc-server-in-white-box.html

不幸的是,它没有完全开箱即用,在追逐错误一段时间之后,我发现服务器无法解码参数(基于squareproc_2的返回码)。 在函数serv_request调用squareproc_2_svc之后,服务器端的执行似乎停止了。 请参阅case: SQUAREPROC中的代码中的SQUAREPROC

 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文件中squareproc_2_svc的实现:

 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

 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,使用rpcgen -a -M生成存根和xdr代码,并使用gcc

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

你有一些问题。

在xen页面中,当它在square_prog_2中执行pthread_create时,它首先调用pthread_attr_setdetachstate,但之前需要执行pthread_attr_init。 此外,attr似乎是静态/全局 – 将它放在函数的堆栈框架中。

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

有一个返回代码11.除了一些特殊的代码,看起来像一个线程上的SIGSEGV。 这将完全由rqstp / transp重叠来解释。

你可能需要重新构建这个,因为我怀疑XDR 不是线程安全的 – 它也不应该是。 另外,我不认为svc_ *是线程安全/知道的。

启动单线程。 作为测试,让square_prog_2直接调用serv_request(例如, 不要执行pthread_ *)。 我打赌这适用于所有模式。

如果是这样,请抓住你的帽子 – 使用线程的示例代码被破坏 – 充满了竞争条件并且会出现段错误等等。如果你没有使用线程挂起(不需要像x *这样的轻型任务) x),你可以按原样欣赏。

否则,解决方案会更复杂一些。 主线程必须对套接字和所有XDR解析/编码进行所有访问。 它不能使用svc_run – 你必须自己滚动。 孩子只能做实际的工作(例如x * x),可能不会触及socket / 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->numptr->num_squared = ptr->num * ptr->num

更新: Linux或FreeBSD似乎支持multithreadingRPC服务器

这是一份文件: https : //www.redhat.com/archives/redhat-list/2004-June/msg00439.html这是一个更清洁的例子。

由此记住 – Linux下不支持rpcgen的选项。 SunOS RPC提供的用于构建multithreadingRPC服务器的库调用在Linux下也不可用

这是Linux rpcgen手册页: http : //linux.die.net/man/1/rpcgen没有提到-M。 IMO,这意味着rpcgen程序有选项并生成存根,但底层支持不存在,因此他们将其从文档中删除。

这是FreeBSD手册页[以及没有支持的原因]: http ://www.freebsd.org/cgi/man.cgi?query = rccgen&sektion = 1&manpath = FreeBSD +5.0-RELEASE请参阅此文档中的-M :

M – 生成multithreading安全的存根,用于在rpcgen生成的代码和用户编写的代码之间传递参数和结果。 此选项对于想要在其代码中使用线程的用户非常有用。 但是,rpc_svc_calls(3)函数还不是MT安全的,这意味着rpcgen生成的服务器端代码不是MT安全的。

另一种方式:

为什么要烦恼RPC / XDR呢? 对于您打算使用的大型arrays来说,开销很大。 大多数标准用途是用于黄页等没有太多数据的东西。

现在大多数系统都是小端的。 只需将本机缓冲区爆炸到直接打开的套接字即可。 在服务器上,让守护进程执行侦听,然后分叉子进程并让子进行接受,读取数据,进行计算并发回回复。 在最坏的情况下,孩子需要进行endian交换,但使用bswap_32可以在紧密的循环中轻松完成。

在每个消息开头的一个简单的小控制结构,在任一方向上都是数据有效负载的前缀:

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

特别说明:我之前已经做过商业化(例如MPI并自己动手),你可能不得不发出setsockopt调用来将内核套接字缓冲区的大小增加到足以维持大量数据的大小

实际上,现在我想到了,如果你不想自己动手,MPI可能会引起人们的兴趣。 然而,使用它,我不是一个真正的粉丝。 它有意想不到的问题,我们不得不删除它,而不是直接控制我们的套接字。