RPC Lab
Remote Procedure Call
Background:
In this lab you will have the opportunity to implement a very basic
version of RPC. The lab will allow you to set up a very simple
remote file system with open, read and write functionality. This
will be built on top of the TCP layer of your protocol stack (if your
TCP layer isnt working, you can potentially try running it on top
of IP). In contrast to previous labs, you will be writing both the
client and server parts of this lab and you will be passing the lab off
against your own code. The following basic procedure will be
followed in running your code:
- The "main" procedure will call the rpc::open(char filename[])
command on the client side
- This will copy the filename into the Data section of an RPC
message with Procedure=RPC_OPEN that will be "pushed" to the server
side through TCP. The main thread will then block on a semaphore until
the response is received.
- The server part of your code will receive this message through
the "pop" interface of RPC and will actually do the open on the file.
- The server part of your code will then "push" the response back
with the handle structure (really just an int) in the "Data" area
- When the client part of your code receives this RPC message
through the "pop" interface from TCP, it should fill in a handle
structure that will be returned from the "open" method in RPC. The
semaphore should then be signalled to allow the "main" thread to
proceed.
- The "main" procedure can then call rpc::read(handle file1, char
buffer[], int len) to read data from this remote file
- This will create a response structure in the RPC object that
will contain the "buffer" address for the response data and a RPC
message with Procedure=RPC_READ will be "pushed" to the server
side through TCP. The main thread will then block on a semaphore until
the response is received.
- The server part of your code will receive this message through
the "pop" interface of RPC and will actually read data from the file.
- The server part of your code will then "push" the response with
the data from the file in the Data part of the message
- When the client part of your code receives this RPC message
through the "pop" interface from TCP, it should copy the data into the
"buffer" area set up in the RPC object. The semaphore should
then be signaled to allow the "main" thread to proceed.
- The "main" procedure then call rpc::write(handle file1, char
buffer[], int len) to write data to the remote file
- This will copy the "buffer" data into the Data part of a
RPC message with Procedure=RPC_WRITE that will be "pushed"
to the server side through TCP. The main thread will then block on a
semaphore until the response is received.
- The server part of your code will receive this message through
the "pop" interface of RPC and will actually write data to the file.
- The server part of your code will then "push" the response.
- When the client part of your code receives this RPC message
through the "pop" interface from TCP, the semaphore should be
signaled to allow the "main" thread to proceed.
Specifications:
You should study the examples and code on pages 405-431 in order to
understand the basic operation of RPC. We will be modeling SunRPC,
except for the fact that SunRPC sits on top of UDP which is on top of
IP. Instead, we will use TCP to guarantee delivery in order.
You have already implemented TCP and IP and the rest of the stack. RPC
will push to TCP and TCP will pop to RPC, in this lab. You can
find example packet breakdowns on page 427 - notice request and reply
are different (a and b of figure 5.20)
The RPC layer object will be similar to the other objects you have
written in prior labs. The RPC layer object must support the
following methods:
- void init ( char *name, protocol *higher,
protocol *lower, char *addr, int unused1, int unused2);
The initialization code will be called with the addr field which
contains the TCP port number and the IP address corresponding to the
destination of this session. You will need this to perform the
push operations on TCP. The TCP port will be the first two bytes
and the IP address will be the last 4 bytes. You should save this
away so that you can send it as the destination to TCP.
- int open ( char *filename);The
open method will:
- copy the filename into the Data section of a RPC message
with Procedure=RPC_OPEN. This message will be passed to
"prot_lower->push" and the thread should block on a response
semaphore. When the semaphore is signaled, the thread should
return the handle defined in the RPC object result area.
- int read ( int handle,
char *buffer, int len );The read method will:
- Create a response structure in the RPC object that will contain
the buffer address for the response data.
- Create an RPC message with Procedure=RPC_READ that will be
passed to lower.push. The desired length will be passed in the
Data area (you can decide on what format to use for the Data
area). The thread should then block on the response semaphore.
- When the semaphore is signaled, the thread should return the
value indicated in the result area of the RPC object.
- int write ( int handle,
char *buffer, int len );The write method will:
- Create an RPC message with Procedure=RPC_WRITE that will be
passed to lower.push. This message will have the buffer
data in its Data area along with the length. The thread should
then block on the response semaphore.
- When the semaphore is signaled, the thread should return the
value indicated in the result area of the RPC object, indicating the
number of bytes that were actually written.
- pop ( char *buf,
int len);When this method is called, you should
- Look at the MsgType field in the RPC
header. If it is REPLY, look at the current RPC method that is
executing (you should keep an entry in your RPC object indicating the
Procedure currently being called.
- If it is a RPC_OPEN, copy the handle from the Data area into
the RPC object result area.
- If it is a RPC_READ, copy the number of bytes read from the
Data area into the result area of the RPC object and copy the file data
from the Data area into the RPC object buffer area.
- If it is a RPC_WRITE, copy the number of bytes written from
the Data area into the result area of the RPC object.
- If the MsgType field is CALL, look at the value of Program.
- If it is a RPC_OPEN, open the filename passed in the Data
area. Create a reply message with the file handle in the Data
area. Call lower.push with this message.
- If it is a RPC_READ, read data from the file handle in the
Data area for the length specified in the data area. Put this data
into a reply message with the actual number of bytes read. Call
lower.push with this message.
- If it is a RPC_WRITE, write data to the file handle in the
Data area for the length specified in the data area. Create a
reply message with the actual number of bytes written in the data area.
Call lower.push with this message.
Passoff: The arguments to the binary are the same as ARP. The
arguments are (host where daemon is running[this machine]) (port daemon
is listening to[1234]) (src IP address) (src ETH address) (dst IP
address) (dst ETH address) (-c or -s) (-v optional debug flag) (0xff
debug level). Pick IP addresses and Ethernet addresses with the same
high order bytes as this example, then change the bottom byte between 4
and 200. We are reserving 2 and 3 for the switches. You should receive
email indicating values that will not be likely to be used by anyone
else.
Example:
On the machine cs460-7 run
./rpc cs460-7 1234 192.168.102.7 00:a0:cc:66:99:33 192.168.102.2
00:a0:cc:66:99:35 -c -v 0xff
On the machine cs460-5 run with exactly the same arguments, except
for the hostname and client flag
./rpc cs460-5 1234 192.168.102.7 00:a0:cc:66:99:33 192.168.102.2
00:a0:cc:66:99:35 -s -v 0xff
While you are getting your code working, you will probably want to
turn on some serious debug statements. If you want to run the lab code
to see how it implemented RPC you can run something like "./rpc cs460-7
1234 192.168.102.1 00:a0:cc:66:99:33 192.168.102.2 00:a0:cc:66:99:35 -c
-v 0xff" it will turn on a lot of debug in the lower layers. Bit 0
turns on the physical layer printfs. Bit 1 (0x2) turns on the ethernet
debug. Bit 2 (0x4) turns on a printf when something is not
destined for your ethernet address. Bit 3 (0x8) turns on the basic arp
debug. Bit 4 (0x10) just shows what is being put into the arp
table. Bit 6 (0x40) turns on the IP debug statements. Bit 7
(0x80) turns on the TCP debug. Bit 8 (0x100) turns on the RPC
debug.
The passoff procedure will test the following specific items
The main()
code will open a remote file .
The main() code
will issue a legal read from the file
The main() code
will issue a read extending beyond the end of the file, you should
actually return only the correct number of available bytes
The main() code
will issue a write to the file beyond the current end of the file
The main() code
will open the file again and issue a read. The number of bytes that
were written in the previous write should be returned.
The passoff version of main.cc can be found in
~cs460ta/labs/rpc_template. Run your code on two machines in order to
pass off your program for the TA during his office hours.
The following passoff levels will apply to this lab:
| Passoff
Level |
Behavior |
Points |
| Minimal
Passoff |
open
and simple read work |
3
Points |
| Write
works |
Write
actually adds data to the file |
4
Points |
| Perfect
Behavior |
Read
beyond end and after write work |
3
Points |