_________________________________________________________________
Looking to buy a house? Get informed with the Home Buying Guide from MSN House & Home. http://coldwellbanker.msn.com/
<h4> Multithreaded RPC Servers for Linux. <br> Thread-safe code versus utilizing static <br> variables to return results from RPC Server <br> to RPC Client. <br> </h4> <h4> Boris A. Derzhavets <br> </h4>
<h4>
This article is supposed to give a positive answer for the question 23.10 from [1] Chapter "RPC".<br>
Actually code published in [1] utilizes static variables what causes question 23.10 from [1]<br>
(about multhreaded implementation of RPC Server under Linux) to produce some confusion. <br>
Originally only two files are taken from [1]: rdict.x and rdict_srp.c. All business logic is implemented into rdict_client.c - file generated by command:<br>
$ rpcgen -a -M rdict.x <br>
Files rdict.c and rdict_cif.c (see [1] , Chapter "RPC") are taken out to highlight the core of RPC technology either "Sun" or "DCE"<br>
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.<br>
I've never got a chance to take a look at "Sun RPC" or "DCE RPC" libraries code <br>
My guess would be that code developed bellow could be very helpfull for Linux developers at this time.<br>
It gives just an example how to manage under Linux. <br>
</h4>
<h4> Source of rdict.x: <br> </h4>
<pre>
const MAXWORD=50;
const DICTSIZ=100;
struct example{
int exfield1;
char exfield2;
};
program RDICTPROG {
version RDICTVERS {
int INITW(void)=1;
int INSERTW(string)=2;
int DELETEW(string)=3;
int LOOKUPW(string)=4;
} =1;
} =0x30090949;
</pre>
<h4>
Source of rdict_srp.c : <br>
</h4>
<pre>
#include<rpc/rpc.h>
#include<string.h>
#include "rdict.h"
char dict[DICTSIZ][MAXWORD+1];
static char snd[50];
static int lns;
int nwords=0;
int
initw()
{
nwords=0;
return 1;
}
int
insertw(char *word)
{
strcpy(dict[nwords],word);
nwords++;
return nwords;
}
int
deletew(char *word)
{
int i;
for(i=0;i<nwords;i++)
if(strcmp(word,dict[i])==0)
{
nwords--;
strcpy(dict[i],dict[nwords]);
return 1;
}
return 0;
}
int
lookupw(char *word)
{
int i;
for(i=0;i < nwords;i++)
if(strcmp(word,dict[i])==0)
return 1;
return 0;
}
rdictprog_1_freeresult(SVCXPRT *transp,xdrproc_t xdr_result,
caddr_t result)
{
xdr_free(xdr_result,result);
return(1);
}
</pre>
<h4>
Call rpcgen to generate stubs ,header file rdict.h and rdict_xdr: <br>
</h4>
<pre>
$rpcgen -a -M rdict.x
</pre>
<h4>
Modified files on server's side follows bellow: <br>
File rdict_sif.c contains thread-safe code instead of utilizing static variables <br>
for to return results to client. <br>
</h4>
<pre>
/*
* rdict_sif.c (compare with file rdict_sif.c in [1] chapter "RPC")
*/
#include<rpc/rpc.h>
#define RPC_SVC
#include "rdict.h"
int initw(void),insertw(char *),deletew(char *),lookupw(char *); bool_t insertw_1_svc(char **w,int *ptr_retcode,struct svc_req *rqstp) { *ptr_retcode=insertw(*(char **)w); return(TRUE); } bool_t initw_1_svc(void *w,int *ptr_retcode,struct svc_req *rqstp) { *ptr_retcode=initw(); return(TRUE); } bool_t deletew_1_svc(char **w,int *ptr_retcode,struct svc_req *rqstp) { *ptr_retcode=deletew(*(char **)w); return(TRUE); } bool_t lookupw_1_svc(char **w,int *ptr_retcode,struct svc_req *rqstp) { *ptr_retcode=lookupw(*(char **)w); return(TRUE); } </pre> <h4> Modified server's stub is file rdict_svc.c .<br> Multithreaded version <br> </h4> <pre> /* Modified rdict_svc.c * * Please do not edit this file. * It was generated using rpcgen. */
#include "rdict.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;
/* Procedure to be run by thread */
void * serv_request(void *data) { struct thr_data { struct svc_req *rqstp; SVCXPRT *transp; } *ptr_data; union { char *insertw_1_arg; char *deletew_1_arg; char *lookupw_1_arg; char *showupw_1_arg; char *getlenw_1_arg; } argument; union { int initw_1_res; int insertw_1_res; int deletew_1_res; int lookupw_1_res; char showupw_1_res; int getlenw_1_res; } result; bool_t retval; xdrproc_t _xdr_argument, _xdr_result; bool_t (*local)(char *, void *, struct svc_req *);
ptr_data = (struct thr_data *)data; struct svc_req *rqstp = ptr_data->rqstp; register SVCXPRT *transp = ptr_data->transp;
switch (rqstp->rq_proc) { case NULLPROC: (void) svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL); return;
case INITW: _xdr_argument = (xdrproc_t) xdr_void; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *))initw_1_svc; break;
case INSERTW: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *))insertw_1_svc; break;
case DELETEW: _xdr_argument = (xdrproc_t) xdr_wrapstring; _xdr_result = (xdrproc_t) xdr_int; local = (bool_t (*) (char *, void *, struct svc_req *))deletew_1_svc; break;
case LOOKUPW:
_xdr_argument = (xdrproc_t) xdr_wrapstring;
_xdr_result = (xdrproc_t) xdr_int;
local = (bool_t (*) (char *, void *, struct svc_req *))lookupw_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 (!rdictprog_1_freeresult (transp, _xdr_result, (caddr_t) &result))
fprintf (stderr, "%s", "unable to free results");
return; }
/* New code for procedure rdictprog_1 , starting thread in response for each clients request to invoke remote procedure */
static void rdictprog_1(struct svc_req *rqstp, register SVCXPRT *transp) { struct data_str { struct svc_req *rqstp; SVCXPRT *transp; } *data_ptr=(struct data_str*)malloc(sizeof(struct data_str)); data_ptr->rqstp = rqstp; data_ptr->transp = transp; pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); pthread_create(&p_thread,&attr,serv_request,(void *)data_ptr); }
int main (int argc, char **argv) { register SVCXPRT *transp;
pmap_unset (RDICTPROG, RDICTVERS);
transp = svcudp_create(RPC_ANYSOCK);
if (transp == NULL) {
fprintf (stderr, "%s", "cannot create udp service.");
exit(1);
}
if (!svc_register(transp, RDICTPROG, RDICTVERS, rdictprog_1, IPPROTO_UDP)) {
fprintf (stderr, "%s", "unable to register (RDICTPROG, RDICTVERS, 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, RDICTPROG, RDICTVERS, rdictprog_1, IPPROTO_TCP)) {
fprintf (stderr, "%s", "unable to register (RDICTPROG, RDICTVERS, tcp).");
exit(1);
}
svc_run (); fprintf (stderr, "%s", "svc_run returned"); exit (1); /* NOTREACHED */ } </pre> <h4>
Compile server: <br>
</h4>
<pre>
$ gcc -o ServerDT rdict_svc.c rdict_sif.c rdict_srp.c rdict_xdr.c -lpthread -lnsl
</pre>
<h4>
Modified code of rdict_client.c with implemented business logic.<br>
Template has been already generated by "rpcgen -a -M rdict.x"<br>
</h4>
<pre>
/* File rdict_client.c versus rdict.c&rdict_cif from [1] chapter "RPC"
*
* This is sample code generated by rpcgen.
* These are only templates and you can use them
* as a guideline for developing your own functions.
*/
#include "rdict.h" #define MAXWORD 50
char buf[80]; void rdictprog_1(char *host) { CLIENT *clnt; enum clnt_stat retval_1; int result_1; char *initw_1_arg="0"; enum clnt_stat retval_2; int result_2; char * insertw_1_arg; enum clnt_stat retval_3; int result_3; char * deletew_1_arg; enum clnt_stat retval_4; int result_4; char * lookupw_1_arg; int ch; char cmd; char word[MAXWORD+1]; int wrdlen;
#ifndef DEBUG clnt = clnt_create (host, RDICTPROG, RDICTVERS, "udp"); if (clnt == NULL) { clnt_pcreateerror (host); exit (1); } #endif /* DEBUG */ while(1) { wrdlen=nextin(&cmd,word); if(wrdlen < 0) exit(0); word[wrdlen]='\0'; switch(buf[0]) { case 'I': retval_1 = initw_1((void*)&initw_1_arg, &result_1, clnt); if (retval_1 != RPC_SUCCESS) { clnt_perror (clnt, "call failed"); } if (result_1 == 1) printf("Dictionary was initialized \n"); else printf("Dictionary failed to initialize \n"); break; case 'i': insertw_1_arg=word; retval_2 = insertw_1(&insertw_1_arg, &result_2, clnt); if (retval_2 != RPC_SUCCESS) { clnt_perror (clnt, "call failed"); } if (result_2 >0 ) printf("Insert was done\n"); else printf("Insert failed\n"); break; case 'd': deletew_1_arg=word; retval_3 = deletew_1(&deletew_1_arg, &result_3, clnt); if (retval_3 != RPC_SUCCESS) { clnt_perror (clnt, "call failed"); } if (result_3 == 1 ) printf("Delete was done\n"); else printf("Delete failed\n"); break; case 'l': lookupw_1_arg=word; retval_4 = lookupw_1(&lookupw_1_arg, &result_4, clnt); if (retval_4 != RPC_SUCCESS) { clnt_perror (clnt, "call failed"); } if (result_4 == 1) printf("Word '%s' was found\n",word); else printf("Word '%s' was not found\n",word); break; case 'q': printf("Programm quits \n"); exit(0); default: printf("Command invalid\n"); break; } } #ifndef DEBUG clnt_destroy (clnt); #endif /* DEBUG */ } int nextin(char *cmd,char *word) { int i,ch; printf("\n"); printf("***** Make a choice ******\n"); printf("1. I(initialize dictionary)\n"); printf("2. i(inserting word) \n"); printf("3. l(looking for word)\n"); printf("4. d(deleting word)\n"); printf("5. q(quit)\n"); printf("***************************\n"); printf("Command prompt =>\t"); ch=getc(stdin); while(isspace(ch)) ch=getc(stdin); if(ch==EOF) return -1; *cmd=(char)ch; sprintf(buf,"%s",cmd); if(buf[0] == 'q' || buf[0] == 'I') return 0; printf("*****************\n"); printf("Analysing Command\n"); printf("*****************\n"); if(buf[0]=='i' || buf[0]=='l'|| buf[0]=='d') { printf("Input word =>\t"); } else { return 0; } ch=getc(stdin); while(isspace(ch)) ch=getc(stdin); if(ch==EOF) return -1; if(ch=='\n') return 0; i=0; while(!isspace(ch)) { if(++i>MAXWORD) { printf("Error word to long.\n"); exit(1); } *word++=ch; ch=getc(stdin); } return i; } int main (int argc, char *argv[]) { char *host;
if (argc < 2) { printf ("usage: %s server_host\n", argv[0]); exit (1); } host = argv[1]; rdictprog_1 (host); exit (0); } </pre> <h4> Compile client: <br> </h4> <pre>
$ gcc -o CientDT rdict_client.c rdict_clnt.c rdict_xdr.c -lnsl </pre> <h4> Now we are ready for testing <br> </h4>
<h4> References. <br>
1.Douglas E. Comer,David L. Stevens Internet Working with TCP/IP ,vol 3 <br>
Client-Server Programming and application Linux/Posix Socket Version, <br>
Prentice Hall,Inc. 2001 <br>
</h4>
_______________________________________________ Redhat-devel-list mailing list Redhat-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/redhat-devel-list