我使用该示例仍然没能得到运行(RPC: Procedure unavailable),通过增加主线程的延时,得到了正确运行。
但是通过增加延时只会加慢RPC服务器处理数据的时间,如果时间过长,会引起RCP调用超时,如果过短,而且RCP调用(新开的线程)还没有处理完成,也是会存在着问题的。我在本地通过延时一秒,并间隔一定时隙调用100次,发
现服务器出现段错误。
我做过将使用指针修改为变量,出现错误:RPC: Server can't decode arguments,后来我发现RPC调用参数的结构体并不是一级指针,而是多级指针。也就是说该结构中还是指针变量成员。
经过试验我总结出RPC调用的时间顺序时:
svc_run()
|-> 获得客户请求
| 为客户数据建立参数
| 调用服务器存根(非多线程)
| 创建一个新线程代替服务器存根执行体 | 新线程开始
| 完成线程创建 | 访问参数
|<----清除参数 | 访问错误 (数据已经清除)
在插入时间等待之后的时间顺序为:
svc_run()
|-> 获得客户请求
| 为客户数据建立参数
| 调用服务器存根(非多线程)
| 创建一个新线程代替服务器存根执行体 | 新线程开始
| 完成线程创建 | 访问参数
| 等待... | 访问用户服务端执行体
|<-----清除参数 | 执行体处理
| .........
| 返回客户端
上图展示了取得等待时间之后的调用顺序,这样就可以成功。如果客户连续请求,而客户端访问参数还没有完成时,将会导致问题,而且这个时间的设定与服务器的处理能力相关,运行负载相关。
为些我想到了使用一个线程锁来解决等待的问题,并且在产生100次连续调用之后,也没有发现问题,但并不代表没有问题,因为对于RPC的运行流程我只是在猜测,而且在返回给客户时,没有一些原始状态信息是否可以正常返回呢
?如果返回数据不是一个简单的数值,而是一个结构体数据,那会怎么样呢?所以希望能看到这时原朋友,如果使用时出现了问题,请告知我。我也会在有时间的时候来测试一下。
如下是服务端代码,是通过rpcgen生成之后再修改的.
- /*
- * Please do not edit this file.
- * It was generated using rpcgen.
- */
- #include <unistd.h>
- #include "square.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <rpc/pmap_clnt.h>
- #include <string.h>
- #include <memory.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #ifndef SIG_PF
- #define SIG_PF void(*)(int)
- #endif
- pthread_t p_thread;
- pthread_attr_t attr;
- #ifdef USING_MT_RPC_WITH_LOCK
- pthread_mutex_t rpcmutex;
- #endif
- void*
- square_prog_thread_func(void* param);
- #ifdef USING_MT_RPC_WITH_LOCK
- void
- square_prog_2_thread_func(
- struct svc_req *rqstp,
- register SVCXPRT *transp,
- pthread_mutex_t* pmutex);
- #else
- void
- square_prog_2_thread_func(
- struct svc_req *rqstp,
- register SVCXPRT *transp);
- #endif
- static void
- square_prog_2(struct svc_req *rqstp, register SVCXPRT *transp)
- {
- /* square_prog_2_thread_func(rqstp, transp);
- return;*/
- struct data_str
- {
- struct svc_req *rqstp;
- SVCXPRT *transp;
- #ifdef USING_MT_RPC_WITH_LOCK
- pthread_mutex_t *pmutex;
- #endif
- } *data_ptr=(struct data_str*)malloc(sizeof(struct data_str));
- if (data_ptr == NULL){
- #ifdef USING_MT_RPC_WITH_LOCK
- square_prog_2_thread_func(rqstp, transp, NULL);
- #else
- square_prog_2_thread_func(rqstp, transp);
- #endif
- return;
- }
- data_ptr-> rqstp = rqstp;
- data_ptr-> transp = transp;
- #ifdef USING_MT_RPC_WITH_LOCK
- data_ptr-> pmutex = &rpcmutex;
- pthread_mutex_lock(&rpcmutex);
- #endif
- /* Create a new thread for client. */
- pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
- pthread_create(&p_thread,&attr,square_prog_thread_func ,(void *)data_ptr);
- #ifdef USING_MT_RPC_WITH_LOCK
- pthread_mutex_lock(&rpcmutex);
- pthread_mutex_unlock(&rpcmutex);
- #else
- sleep(1);
- #endif
- }
- void*
- square_prog_thread_func(void* param)
- {
- struct thr_data
- {
- struct svc_req *rqstp;
- SVCXPRT *transp;
- #ifdef USING_MT_RPC_WITH_LOCK
- pthread_mutex_t *pmutex;
- #endif
- } *ptr_data;
- /* recover param from thread param */
- ptr_data = (struct thr_data *)param;
- struct svc_req *rqstp = (ptr_data-> rqstp);
- register SVCXPRT *transp = (ptr_data-> transp);
- /* continue server processing ... */
- #ifdef USING_MT_RPC_WITH_LOCK
- square_prog_2_thread_func(rqstp, transp, ptr_data->pmutex);
- #else
- square_prog_2_thread_func(rqstp, transp);
- #endif
- free(param);
- return NULL;
- }
- #ifdef USING_MT_RPC_WITH_LOCK
- void
- square_prog_2_thread_func(
- struct svc_req *rqstp,
- register SVCXPRT *transp,
- pthread_mutex_t* pmutex)
- #else
- void
- square_prog_2_thread_func(
- struct svc_req *rqstp,
- register SVCXPRT *transp)
- #endif
- {
- union {
- square_in squareproc_2_arg;
- } argument;
- union {
- square_out squareproc_2_res;
- } result;
- bool_t retval;
- xdrproc_t _xdr_argument, _xdr_result;
- bool_t (*local)(char *, void *, struct svc_req *);
- switch (rqstp->rq_proc) {
- case NULLPROC:
- (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;
- local = (bool_t (*) (char *, void *, struct svc_req *))squareproc_2_svc;
- break;
- default:
- svcerr_noproc (transp);
- return;
- }
- memset ((char *)&argument, 0, sizeof (argument));
- if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
- svcerr_decode (transp);
- return;
- }
- /* unlock rpc call thread */
- #ifdef USING_MT_RPC_WITH_LOCK
- if (pmutex) pthread_mutex_unlock(pmutex);
- #endif
- /* call user define function */
- retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp);
- if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) {
- 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;
- }
- int
- main (int argc, char **argv)
- {
- register SVCXPRT *transp;
- #ifdef USING_MT_RPC_WITH_LOCK
- /* Create a mutex object for rpc calling */
- pthread_mutex_init(&rpcmutex, NULL);
- #endif
- pmap_unset (SQUARE_PROG, SQUARE_VERS);
- transp = svcudp_create(RPC_ANYSOCK);
- if (transp == NULL) {
- fprintf (stderr, "%s", "cannot create udp service.");
- exit(1);
- }
- if (!svc_register(transp, SQUARE_PROG, SQUARE_VERS, square_prog_2, IPPROTO_UDP)) {
- fprintf (stderr, "%s", "unable to register (SQUARE_PROG, SQUARE_VERS, udp).");
- exit(1);
- }
- transp = svctcp_create(RPC_ANYSOCK, 0, 0);
- if (transp == NULL) {
- fprintf (stderr, "%s", "cannot create tcp service.");
- exit(1);
- }
- if (!svc_register(transp, SQUARE_PROG, SQUARE_VERS, square_prog_2, IPPROTO_TCP)) {
- fprintf (stderr, "%s", "unable to register (SQUARE_PROG, SQUARE_VERS, tcp).");
- exit(1);
- }
- svc_run ();
- fprintf (stderr, "%s", "svc_run returned");
- exit (1);
- /* NOTREACHED */
- }
在代码的#70行处,可以看到在准备为客户求建立一个线程,在创建完线程之后,在#73行开始等待锁,也就是在这个时候等待参数被RPC解析并到调用服务端处理代码期间。在#152行,我释放了锁,这样在主线程就可以返回了,而辅助线程将进入自定义的服务器处理函数。这样就实现了一个比较合理的等待。
下面将程序代码以及MAKEFILE文件发到此处,希望需要这个方面信息的朋友可以测试一下,并提出更多的想法。
square.x
- struct square_in {
- long arg1;
- };
- struct square_out {
- long res1;
- };
- program SQUARE_PROG {
- version SQUARE_VERS {
- square_out SQUAREPROC(square_in) = 1;
- /* procedure number = 1 */
- } = 2; /* version number = 2 */
- } = 0x31230000; /* program number = 0x31230000 */
server.c 服务务用户处理函数所在地
- #include <unistd.h>
- #include <pthread.h>
- #include "square.h"
- bool_t
- squareproc_2_svc(square_in *inp, square_out *outp, struct svc_req *rqstp)
- {
- printf("thread %ld started, arg = %ld/n",
- pthread_self(), inp->arg1);
- sleep(5);
- outp->res1 = inp->arg1 * inp->arg1;
- printf("thread %ld done/n", pthread_self());
- return(TRUE);
- }
- int
- square_prog_2_freeresult(SVCXPRT *transp, xdrproc_t xdr_result,
- caddr_t result)
- {
- xdr_free(xdr_result, result);
- return(1);
- }
client.c 客户调用,在这里希望注意的就是clnt_destroy函数的调用,尤其是在错误的时候没有调用时,将导致客户与服务器之间保持了一个连接(在LINUX上表示找开了一个文件),如果出现多次,将在服务器上相当于打开了许多的TCP连接(文件),将导致服务器程序因为受操作系统的进程打开文件数限制而出错(打开文件时)。所以请一定要注意!
- #include <rpc/rpc.h>
- #include "square.h"
- int
- main(int argc, char **argv)
- {
- CLIENT *cl;
- square_in in;
- square_out out;
- if (argc != 3){
- printf("usage: client <hostname> <integer-value>");
- exit(0);
- }
- cl = clnt_create(argv[1], SQUARE_PROG, SQUARE_VERS, "tcp");
- in.arg1 = atol(argv[2]);
- if (squareproc_2(&in, &out, cl) != RPC_SUCCESS){
- printf("%s", clnt_sperror(cl, argv[1]));
- clnt_destroy(cl);
- exit(0);
- }
- clnt_destroy(cl);
- printf("result: %ld/n", out.res1);
- exit(0);
- }
Makefile 编译文件
- PROGS = client server
- #cancele when you don't need thread lock to use mulitthread
- MACRO = -DUSING_MT_RPC_WITH_LOCK
- CFLAGS = -g -dH -Wall -DDEBUG $(MACRO)
- #CFLAGS = -Wall
- LINKFLAGS = -lpthread
- CC = gcc
- all: ${PROGS}
- square.h square_clnt.c square_svc.c square_xdr.c: square.x
- rpcgen -C -M square.x
- square_clnt.o: square_clnt.c square.h
- square_svc.o: square_svc.c square.h
- client: square.h client.o square_clnt.o square_xdr.o
- ${CC} ${CFLAGS} -o $@ client.o square_clnt.o square_xdr.o
- server: square.h server.o square_svc.o square_xdr.o
- ${CC} ${CFLAGS} -o $@ server.o square_svc.o square_xdr.o $(LINKFLAGS)
- clean:
- rm -f *o
编译方法,先使用make命令编译程序,然后再将square_svc.c文件覆盖,再编译,方能实现多线程。
最后,我发现,如果客户端过早终止,服务器将出错。问题出在square_svc.c文件#156行,在给TCP回复的时候出的问题:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1208173680 (LWP 28672)]
0x00ac7ef5 in writetcp () from /lib/libc.so.6
(gdb) where
#0 0x00ac7ef5 in writetcp () from /lib/libc.so.6
#1 0x00aca570 in xdrrec_endofrecord_internal () from /lib/libc.so.6
#2 0x00ac7e00 in svctcp_reply () from /lib/libc.so.6
#3 0x00ac682c in svc_sendreply_internal () from /lib/libc.so.6
#4 0x08048b39 in square_prog_2_thread_func (rqstp=0xbfe9af18, transp=0x8f36708, pmutex=0x80491a4)
at square_svc.c:156
#5 0x08048a3a in square_prog_thread_func (param=0x8f33008) at square_svc.c:99
#6 0x00b462db in start_thread () from /lib/libpthread.so.0
#7 0x00aa014e in clone () from /lib/libc.so.6
(gdb)
是线程在完成服务端用户函数调用准备给启用返回结果时出现的。按理应该不是出现段错误的。
如果需要服务器工作稳定的话,需要增加去这个错误的处理。希望能解决的朋友帮忙解决一下。
refs: http://blog.csdn.net/pbymw8iwm/article/details/8623106
http://blog.csdn.net/chongyangqu/article/details/4367781