Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  Extensions to the Sun RPC Library Non-Blocking I/O Using Non-Blocking I/O Using a simple counter with non-blocking I/O  Previous   Contents   Next 
   
 

clnt_call() Configured as Non-Blocking

For a one-way message, use the clnt_send() function. No time-out is applied as the client sends a request to a server and does not wait for a reply.

For two-way messaging, use clnt_call(). The client remains blocked until the server sends a reply or an error status message, or until a time-out occurs at the client side.

The non-blocking feature enables you to send two-way and one-way calls together. If you use clnt_call() on the client side configured as non-blocking, that is, using the RPC_CL_NONBLOCKING I/O MODE, you get the following modified behavior. When a two-way request is sent to the buffer, all one-way requests already in the buffer are sent through the transport layer before the two-way request is processed. The time taken to empty the buffer is not counted in the two-way call timeout. For further information, see the clnt_control(3NSL) man page.

Client Connection Closure Callback

Client connection closure callback enables the server for connection-oriented transport to detect that the client has disconnected. The server can take the necessary action to recover from transport errors. Transport errors occur when a request arrives at the server, or when the server is waiting for a request and the connection is closed.

The connection closure callback is called when no requests are currently being executed on the connection. If the client connection is closed when a request is being executed, the server executes the request but a reply may not be sent to the client. The connection closure callback is called when all pending request are completed.

When a connection closure occurs, the transport layer sends an error message to the client. The handler is attached to a service using svc_control() for example as follows:
svc_control(service, SVCSET_RECVERRHANDLER, handler);

The arguments of svc_control() are:

  1. A service or an instance of this service. When this argument is a service, any new connection to the service inherits the error handler. When this argument is an instance of the service, only this connection gets the error handler.

  2. The error handler callback. The prototype of this callback function is:
    void handler(const SVCXPRT *svc, const boot_t IsAConnection);

For further information see the svc_control(3NSL) man page.


Note - For XDR unmarshalling errors, if the server is unable to unmarshal a request, the message is destroyed and an error is returned directly to the client.


Example of client connection closure callback

This example implements a message log server. A client can use this server to open a log (actually a text file), to store message log, and then to close the log.

The log.x file describes the log program interface.

enum log_severity { LOG_EMERG=0, LOG_ALERT=1, LOG_CRIT=2, LOG_ERR=3,
		    LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6 };

program LOG { 
			  version LOG_VERS1 {
						  int OPENLOG(string ident) = 1;

						  int CLOSELOG(int logID) = 2;

						  oneway WRITELOG(int logID, log_severity severity,
				string message) = 3;
			  } = 1;
} = 0x20001971;

The two procedures OPENLOG and CLOSELOG open and close a log that is specified by its logID. The WRITELOG() procedure, declared as oneway for the example, logs a message in an opened log. A log message contains a severity attribute, and a text message.

This is the makefile for the log server. Use this makefile to call the log.x file.

RPCGEN = rpcgen

CLIENT = logClient
CLIENT_SRC = logClient.c log_clnt.c log_xdr.c
CLIENT_OBJ = $(CLIENT_SRC:.c=.o) 

SERVER = logServer
SERVER_SRC = logServer.c log_svc.c log_xdr.c
SERVER_OBJ = $(SERVER_SRC:.c=.o)

RPCGEN_FILES = log_clnt.c log_svc.c log_xdr.c log.h

CFLAGS += -I.

RPCGEN_FLAGS	= -N -C
LIBS = -lsocket -lnsl

all: log.h ./$(CLIENT) ./$(SERVER)


$(CLIENT): log.h $(CLIENT_OBJ)
			  cc -o $(CLIENT) $(LIBS) $(CLIENT_OBJ)  

$(SERVER): log.h $(SERVER_OBJ) 
			  cc -o $(SERVER)  $(LIBS) $(SERVER_OBJ)  

$(RPCGEN_FILES): log.x
			  $(RPCGEN) $(RPCGEN_FLAGS) log.x

clean:
			  rm -f $(CLIENT_OBJ) $(SERVER_OBJ) $(RPCGEN_FILES)

logServer.c shows the implementation of the log server. As the log server opens a file to store the log messages, it registers a closure connection callback in openlog_1_svc(). This callback is used to close the file descriptor even if the client program forgets to call the closelog() procedure (or crashes before doing so). This example demonstrates the use of the connection closure callback feature to free up resources associated to a client in an RPC server.

#include "log.h"
#include <stdio.h>
#include <string.h>

#define NR_LOGS 3

typedef struct {
    SVCXPRT* handle;
    FILE* filp;
    char* ident;
} logreg_t;


static logreg_t logreg[NR_LOGS];
static char* severityname[] = {"Emergency", "Alert", "Critical", "Error",
                               "Warning", "Notice", "Information"};

    static void
close_handler(const SVCXPRT* handle, const bool_t);


    static int
get_slot(SVCXPRT* handle)
{
    int i;
    
    for (i = 0; i < NR_LOGS; ++i) {
        if (handle == logreg[i].handle) return i;
    }
    return -1;
}

    static FILE*
_openlog(char* logname)
/*
 * Open a log file
 */
{
    FILE* filp = fopen(logname, "a");
    time_t t;

    if (NULL == filp) return NULL;
    
    time(&t);
    fprintf(filp, "Log opened at %s\n", ctime(&t));

    return filp;
}

    static void
_closelog(FILE* filp)
{
    time_t t;

    time(&t);
    fprintf(filp, "Log close at %s\n", ctime(&t));
		/*
 	 * Close a log file
 	 */
    fclose(filp);
}
    
    int*
openlog_1_svc(char* ident, struct svc_req* req)
{
    int slot = get_slot(NULL);
    FILE* filp;
    static int res;
    time_t t;
    
    if (-1 != slot) {
        FILE* filp = _openlog(ident);
        if (NULL != filp) {
            logreg[slot].filp = filp;
            logreg[slot].handle = req->rq_xprt;
            logreg[slot].ident = strdup(ident);

		/*
 	 * When the client calls clnt_destroy, or when the 
 	 * client dies and clnt_destroy is called automatically, 
 	 * the server executes the close_handler callback
 	 */
            if (!svc_control(req->rq_xprt, SVCSET_RECVERRHANDLER,
			     							(void*)close_handler)) {
						 puts("Server: Cannot register a connection closure callback");
						 exit(1);
	    			}
        }
        
    }
    res = slot;
    return &res;
}

    int*
closelog_1_svc(int logid, struct svc_req* req)
{
    static int res;

    if ((logid >= NR_LOGS) || (logreg[logid].handle != req->rq_xprt)) {
        res = -1;
        return &res;
    }
    logreg[logid].handle = NULL;
    _closelog(logreg[logid].filp);
    res = 0;
    return &res;
}

/*
 * When there is a request to write a message to the log, 
 * write_log_1_svc is called
 */
    void*
writelog_1_svc(int logid, log_severity severity, char* message,
               struct svc_req* req)
{
    if ((logid >= NR_LOGS) || (logreg[logid].handle != req->rq_xprt)) {
        return NULL;
    }
		/*
		 * Write message to file
		 */
    fprintf(logreg[logid].filp, "%s (%s): %s\n",
            logreg[logid].ident, severityname[severity], message);
    return NULL;
}

    static void
close_handler(const SVCXPRT* handle, const bool_t dummy)
{
    int i;
    
		/* 
		 * When the client dies, the log is closed with closelog
		 */
    for (i = 0; i < NR_LOGS; ++i) {
        if (handle == logreg[i].handle) {
            logreg[i].handle = NULL;
            _closelog(logreg[i].filp);
        }
    }
}
 
 
 
  Previous   Contents   Next