Multithreading RPC Servers for Linux.Thread-safe code (the most recent version)

"Boris Derzhavets" <[email protected]> Wed, 02 Jun 2004 13:00:54 +0000
Newsgroups gmane.linux.redhat.devel
Message-ID <[email protected]>

_________________________________________________________________
Looking to buy a house? Get informed with the Home Buying Guide from MSN 
House & Home. http://coldwellbanker.msn.com/

_______________________________________________
Redhat-devel-list mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/redhat-devel-list
RPC_ServerBuild.html (text/plain, 11.8 KB)
<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&ltrpc/rpc.h&gt
#include&ltstring.h&gt
#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&ltnwords;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 &lt 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&ltrpc/rpc.h&gt
#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 &ltstdio.h&gt
#include &ltstdlib.h&gt
#include &ltrpc/pmap_clnt.h&gt
#include &ltstring.h&gt
#include &ltmemory.h&gt
#include &ltsys/socket.h&gt
#include &ltnetinet/in.h&gt

#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-&gtrqstp;
register SVCXPRT *transp = ptr_data-&gttransp;

switch (rqstp-&gtrq_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 &gt 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-&gtrqstp = rqstp;
data_ptr-&gttransp = 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 &lt 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 &gt0 )
      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 =&gt\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  =&gt\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&gtMAXWORD)
                {
                        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 &lt 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>