next up previous contents
Next: Server implementation Up: A file server Previous: A file server

Design of interface

Our file server should provide a set of RPC functions that allow a client to access files located at the server machine. The server would open the file on client's behalf, and subsequently, read from or write to the file when such operations are requested from the client.

It will be convenient to make our RPC functions look like existing file I/O functions. For example, we could make our RPC interface resembles the UNIX file I/O system calls, read(), write(), and chdir(), etc. For our demo, let's mimic the C stdio library functions, such as fread() and fwrite(). To distinguish our RPC from the C library functions, we prefix our functions with a lower case 'r', so our RPCes will be rfread(), rfwrite(), etc , we will define a type called rFILE corresponding to the FILE type. So the rfread() function is of the following prototype,

   int rfread(void * buf, int size, int nmemb, rFILE* stream);

of course, we should also have a rfopen() function to open a remote file.

Assuming an RPC connection has been established, a client could read from a file "foo" sitting on the server's machine like this,

     rFILE * fp;
     char    buf[1024];
     fp = rfopen("foo", "r");
     rfread(buf, 1, 1024, fp);

Having decide the interface functions, we need to decide the transport protocol. UDP is unreliable and the size of datagrams are usually limited to 8K, so let's choose TCP.

Another decision we need to make is the statefulness of the server. A stateful server maintains the state for the client, whereas in a stateless server, the client supplies all the information (such as current file offset) at every RPC. In our demo, we choose to use a stateful implementation.

The stateful server works as follows. The server maintains a table of opened files, when the client makes a rfopen() call, the server uses fopen() to open the file and record the FILE* pointer in the table, the index to that table entry is return to the client. The client then use the index to reference the file when it makes rfread(), rfwrite() and rfclose() calls later. Obviously, This index must be a field of our rFILE structure.

To implement our client program (simple FTP), we also need an RPC to list the contents of the remote directory. Thus we have the rlistdir() function, which returns a linked list of directory entries in its argument.

Combining these ideas we come up with the following interface declaration,

typedef  char [size=strlen(*this)+1, 1024]  str1024;
typedef  char  c_arr1024 [size=strlen(*this)+1, 1024];
typedef  unsigned long size_T;

typedef struct fHandle {
       int fd; // identify the file on server
}rFILE;

typedef struct dentry {
                c_arr1024      name; 
                struct dentry *next;
}DENTRY;

interface rfile {
     property TRANSPORT_PROTOCOL= tcp;
     property INIT_BEFORE_REGISTER= init_fdtable;

     rFILE* rfopen(in const str1024 filename, 
                   in const str1024 mode)
            {
                property     TIMEOUT_VALUE = 2;
            };

     int    rfread
            (out void [maxsize=size*nm, size=return>0?return:0] ptr,
             size_T size, size_T nm, in rFILE* stream
             );
     int    rfwrite
            (in const void [maxsize=size*nm, size=nm*size] ptr,
             size_T size, size_T nm, in rFILE* stream);
     int    rfclose(rFILE*stream) ;
     int    rlistdir(in str1024 path, out DENTRY * pent);
     int    rchdir(in str1024 path) ;
} 0x5555 ;

Note we defined the property INIT_BEFORE_REGISTER, which is the function to initialize the table of FILE* pointers to 0 on the server.


next up previous contents
Next: Server implementation Up: A file server Previous: A file server

Copyright (C) Netbula LLC, 1996