C语言 Linux 上的 RPC 多线程服务器 - Pthreads



我正在为大学做一个RPC项目。我必须实现一个 RPC 多线程服务器。我一直在一台服务器上工作,只有一个过程对两个数字求和。我的代码位于:https://github.com/alvmatias/pdytr

这就是我处理客户端请求的方式

/* Register RPC Service */
/* serve client's requests asynchronously */
while(1){
rfds = svc_fdset;
/* get max value that newly created file descriptor can have in "nfds" */
switch (select(nfds, &rfds, NULL,NULL,NULL)){
case -1:   
case 0 :
break;
default: 
/* Handle RPC request on each file descriptor */
svc_getreqset(&rfds);
}

然后我创建线程:

static void ej_prg_1(struct svc_req *rqstp, register SVCXPRT *transp)
{
/* 2 threads possible for now */
pthread_t th[2];
pthread_attr_t attr[2];
static int id=0;
/* Used to pass arguments to "funcionA" */
struct data_str{
struct svc_req *rqstp;
SVCXPRT *transp;
int id;
} *data_ptr=(struct data_str*)malloc(sizeof(struct data_str));
/* Set parameters */
data_ptr-> rqstp = rqstp;
data_ptr-> transp = transp;
data_ptr-> id = id;
/* Create thread */
printf("Crating thread %dn", id);
pthread_attr_init(&attr[id]);
pthread_attr_setdetachstate(&attr[id],PTHREAD_CREATE_DETACHED);
pthread_create(&th[id],&attr[id],&funcionA,(void *)data_ptr);
printf("Thread %d createdn", id);
id=(id+1)%2;
}

这是每个线程执行的函数:

void *funcionA(void *data) {
/* Structure for parameters */
struct thr_data{
struct svc_req *rqstp;
SVCXPRT *transp;
int id;
} *ptr_data; 
union {
operands add_1_arg;
} argument;
union {
int add_1_res;
} result;
bool_t retval;
xdrproc_t _xdr_argument, _xdr_result;
bool_t (*local)(char *, void *, struct svc_req *);
/* Get the parameters */
ptr_data = (struct thr_data  *)data;
struct svc_req *rqstp = ptr_data-> rqstp;
register SVCXPRT *transp = ptr_data-> transp;
printf("Hello thread: %dn", ptr_data-> id);
/*Code generated by rpcgen */
switch (rqstp->rq_proc) {
case NULLPROC:
(void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
return;
case ADD:
_xdr_argument = (xdrproc_t) xdr_operands;
_xdr_result = (xdrproc_t) xdr_int;
local = (bool_t (*) (char *, void *,  struct svc_req *))add_1_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;
}
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 (!ej_prg_1_freeresult (transp, _xdr_result, (caddr_t) &result)){
fprintf (stderr, "%s", "unable to free results");
}
/* End of rpcgen code */
/* Exit thread */ /* Remember it's detached */
printf("Bye thread: %dn", ptr_data-> id);
pthread_exit(0);
}

这是远程过程:

bool_t add_1_svc(operands *argp, int *p, struct svc_req *rqstp)
{
bool_t result;
printf("Got request: adding %d, %dn", argp->x, argp->y);
/* Add two numbers */
*p=argp->x + argp->y;
/* Execute a 4 seconds sleep to check multithreading */
sleep(4);
/* Return ok */
result = 1;
return (result);
}

我的问题是我执行两个客户端,例如:

./ej_client 本地主机 20 20 ./ej_client 本地主机 30 30

两者都得到 60 作为答案,而不是预期的 40 和 60。我的程序有什么问题?

看了这个问题,答案说 -M 标志确实生成了存根,但svc_calls Linux 下不是 MT 安全的。这是否意味着多线程服务器将无法工作?RPC 无法解码 TCP 传输的参数

编辑:问题似乎就在这里

if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) {
svcerr_systemerr (transp);
}

我认为"transp"被最新的调用覆盖。因此,进行最新调用的客户端会收到前一个调用的应答。我该怎么做才能解决这个问题?

编辑2:我发现:在当前实现中,服务传输句柄SVCXPRT包含一个用于解码参数和编码结果的数据区域。因此,此结构不能在调用执行此操作的函数的线程之间自由共享。检查:https://docs.oracle.com/cd/E19683-01/816-0214/6m6nf1p6o/index.html 我该怎么做才能使其 MT 安全,以便它可以在多个线程之间自由共享?

谢谢,马蒂亚斯。

svc_getargs() 不是线程安全的。 此函数从主 RPC 线程复制数据。 当您调用此函数时,它会复制当前请求数据,您希望从回调函数调用此函数,将数据复制到结构体,然后在线程中使用数据。否则,您可能会看到一些不可预测的结果。

我会使用 rpcgen -MNC <.x 文件名>然后创建一个包装函数,调用 svc_getargs() 复制当前请求数据,然后将数据通过结构变量传递给线程进行处理

最新更新