Introduction to programming with Sun/ONC RPC: step 6
Step 1 | Step 2 | Step 3 | Step 4 | Step 5 | Step 6 |
Step 6. Cleanup
Your program is now working. All it needs now is some cleaning up. This is a crucial step for any non-throw-away code. You want to make sure the program is readable and flows well. It shouldn't have the appearance that you just hacked some code from a template just to get it working. The overall style should be consistent between your code and the rpcgen-generated code. For example:
- I do not add a space before the opening parenthesis on a function call (e.g. "clnt_destroy(clnt)" instead of "clnt_destroy (clnt)", so I removed them here.
- A name such as add_1_arg is not only unclear but rather long. It's as bad as naming the first parameter to function f as f_arg. I'm just going to call it v (for value; the code is so short that the meaning is clear and doesn't need the verbosity that a more global variable would call for).
- It's important that you exit the program or otherwise ensure that you do not make any more RPC calls if you received an error from an RPC call (a return of 0).
- I like having my main function at the top, so I moved it up and declared the function prototypes above it.
- I moved the call to clnt_destroy to the main function. This allows one to call add multiple times without having it destroy the client handle after the first call.
- I created a function called rpc_setup to create the client handle. It's only a few calls but it makes the main function cleaner to follow.
- I added #include <stdio.h> at the top of the file to avoid errors of an undefined stdio.h (some systems gripe more than others).
- I added #include <stdlib.h> at the top of the file to keep the compiler on my mac from giving warnings about the implicit definition of exit. You may not need this on other systems.
- I removed any comments about this being template code.
The #ifdef DEBUG statements should be removed and the code can be made easier to read if the main function is moved to the top. Variables can be renamed more sensibly (add_1_arg is a bit crude). Auto-generated comments should be removed.
If we really intended to use the add remote procedure call repeatedly we would not want to create the handle each time, so it would be a good idea to create the RPC handle just once.
Here is the final client code:
/* RPC example: add two numbers */ #include "add.h" CLIENT *rpc_setup(char *host); void add(CLIENT *clnt, int a, int b); int main(int argc, char *argv[]) { CLIENT *clnt; /* client handle to server */ char *host; /* host */ int a, b; if (argc != 4) { printf("usage: %s server_host num1 num2\n", argv[0]); exit(1); } host = argv[1]; if ((a = atoi(argv[2])) == 0 && *argv[2] != '0') { fprintf(stderr, "invalid value: %s\n", argv[2]); exit(1); } if ((b = atoi(argv[3])) == 0 && *argv[3] != '0') { fprintf(stderr, "invalid value: %s\n", argv[3]); exit(1); } if ((clnt = rpc_setup(host)) == 0) exit(1); /* cannot connect */ add(clnt, a, b); clnt_destroy(clnt); exit(0); } CLIENT * rpc_setup(char *host) { CLIENT *clnt = clnt_create(host, ADD_PROG, ADD_VERS, "udp"); if (clnt == NULL) { clnt_pcreateerror(host); return 0; } return clnt; } void add(CLIENT *clnt, int a, int b) { int *result; intpair v; /* parameter for add */ v.a = a; v.b = b; result = add_1(&v, clnt); if (result == 0) { clnt_perror(clnt, "call failed"); } else { printf("%d\n", *result); } }
And the server (with one line of diagnostics being printed) is:
Even for a trivially simple program like this there are several ways to format the output. I opt for a single number on one line:
the number on a single line rather than something like:
or
Question: Why?
One last thing
Before we're completely done with this, let's create a new makefile. Unfortunately the automatically-generated one is neither easy to read nor reliable in its list of dependencies. We'll write a very explicit one. This is how our makefile looks now: