20.5 Request-Reply with Timeouts and Retries

Team-FLY

The client of Program 20.4 can hang indefinitely if either the request message or the reply message is lost or if the server crashes. The client can use timeouts to handle these potential deadlocks. Before making a blocking call, the process sets a timer that generates a signal to interrupt the call after a certain length of time. If the interrupt occurs, the process can try again or use a different strategy.

You can implement a timeout directly by setting a software timer or by using timeout facilities included as options to calls such as select . Sockets themselves have some options for setting timeouts. Section 20.7 discusses the pros and cons of different timeout strategies.

The u_recvfromtimed function of UICI UDP provides a simple interface to these timeout facilities. The u_recvfromtimed function is similar to u_recvfrom , but it takes an additional double parameter, time , indicating the number of seconds to block, waiting for a response. After blocking for time seconds without receiving a response on the specified endpoint, u_recvfromtimed returns “1 and sets errno to ETIME . For other errors, u_recvfromtimed returns “1 and sets the errno as u_recvfrom does.

Program 20.6 modifies Program 20.4 to call the function request_reply_timeout , shown in Program 20.7, instead of calling request_reply . A third command-line argument to this program specifies the number of seconds to wait before timing out.

Program 20.6 client_udp_request_reply_timeout.c

A client program that uses timeouts with request-reply .

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "restart.h" #include "uiciudp.h" #define BUFSIZE 1024 int request_reply_timeout(int requestfd, void* request, int reqlen,                   char* server, int serverport, void *reply, int replen,                   double timeout); int main(int argc, char *argv[]) {    ssize_t bytesread, byteswritten;    char reply[BUFSIZE];    char request[BUFSIZE];    int requestfd;    u_port_t serverport;    double timeout;    if (argc != 4) {       fprintf(stderr, "Usage: %s servername serverport timeout\n", argv[0]);       return 1;    }    serverport = (u_port_t) atoi(argv[2]);    timeout = atof(argv[3]);    if ((requestfd = u_openudp(0)) == -1) {     /* create unbound UDP endpoint */       perror("Failed to create UDP endpoint");       return 1;    }    sprintf(request, "[%ld]\n", (long)getpid());    /* create a request string */                  /* use request-reply protocol with timeout to send a message */    bytesread = request_reply_timeout(requestfd, request, strlen(request) + 1,                         argv[1], serverport, reply, BUFSIZE, timeout);    if (bytesread == -1)       perror("Failed to complete request_reply_timeout");    else {       byteswritten = r_write(STDOUT_FILENO, reply, bytesread);       if (byteswritten == -1)          perror("Failed to echo server reply");    }    if ((r_close(requestfd) == -1)  (bytesread == -1)  (byteswritten == -1))       return 1;    return 0; } 
Program 20.7 request_reply_timeout.c

Request-reply implementation with timeout .

 #include <sys/types.h> #include "uiciudp.h" int request_reply_timeout(int requestfd, void* request, int reqlen,                   char* server, int serverport, void *reply, int replen,                   double timeout) {    ssize_t nbytes;    u_buf_t senderinfo;                                                         /* send the request */    nbytes = u_sendtohost(requestfd, request, reqlen, server, serverport);    if (nbytes == -1)       return -1;        /* wait timeout seconds for a response, restart if from wrong server */    while ((nbytes = u_recvfromtimed(requestfd, reply, replen,                                          &senderinfo, timeout)) >= 0 &&                 (u_comparehost(&senderinfo, server, serverport) == 0)) ;    return (int)nbytes; } 

Figure 20.7 shows a state diagram for the request-reply logic of Program 20.7. The circles represent functions calls that implement the major steps in the protocol, and the arrows indicate outcomes .

Figure 20.7. State diagram of the client for request-reply with simple timeout.

graphics/20fig07.gif

The request_reply_timeout function of Program 20.7 returns an error if the server does not respond after an interval of time. Either the request was not serviced or it was serviced and the reply was lost or never sent. The client cannot distinguish between a lost message and a server crash.

Another potential problem is that Program 20.7 resets the timeout each time it encounters an incorrect responder . In a denial-of-service attack , offenders continually send spurious packets to ports on the attacked machine. Program 20.7 should limit the number of retries before taking some alternative action such as informing the user of a potential problem.

Exercise 20.12

Request-reply protocols can also be implemented over TCP. Why are these implementations usually simpler than UDP implementations ? Are there disadvantages to a TCP implementation?

Answer:

Since TCP provides an error-free stream of bytes, the application can use the error-free request-reply protocol shown in Figure 20.5. Another advantage of TCP implementations is that the client has a connection to the server and can signal that it is finished by closing this connection. The server can then release resources that it has allocated to servicing that client's requests . The client can also detect a server crash while it is waiting for a reply. On the downside, TCP implementations incur overhead in setting up the connection.

Usually, implementations of request-reply with timeout have a mechanism for retrying the request a certain number of times before giving up. The state diagram of Figure 20.8 summarizes this approach. The user specifies a maximum number of retries. The application retries the entire request-reply sequence each time a timeout occurs until the number of retries exceeds the specified maximum.

Figure 20.8. Request-reply with timeouts.

graphics/20fig08.gif

Program 20.8 implements the request-reply protocol of Figure 20.8 for use in a client similar to Program 20.6.

Exercise 20.13

How would the client in Program 20.6 need to be modified to use the protocol in Program 20.8?

Answer:

The client would have to take an extra command-line argument for the number of retries and call request_reply_timeout_retry instead of request_reply_timeout .

Exercise 20.14

Propose a more sophisticated method of handling timeouts than that of Program 20.8. How might a potential infinite loop due to wrong host be handled?

Answer:

The selection of a timeout value is somewhat arbitrary. If the timeout value is large, the application may wait too long before recognizing a problem. However, timeout values that are too short do not account for natural delays that occur in transit over a network. A more sophisticated timeout strategy would lengthen the timeout value on successive retries and perhaps keep statistics about response times to use in setting future timeout values. Often, the timeout value is doubled for each successive timeout. The potential infinite loop for the wrong host might be handled by incorporating a counter for the wrong host condition and returning an error if this condition occurs more than a certain number of times.

Program 20.8 request_reply_timeout_retry.c

Request-reply implementation with timeout and retries .

 #include <stdio.h> #include <errno.h> #include "uiciudp.h" int request_reply_timeout_retry(int requestfd, void* request, int reqlen,                   char* server, int serverport, void *reply, int replen,                   double timeout, int maxretries) {    ssize_t nbytes;    int retries;    u_buf_t senderinfo;    retries = 0;    while (retries < maxretries) {                                    /* send process ID to (server, serverport) */        nbytes = u_sendtohost(requestfd, request, reqlen, server, serverport);        if (nbytes == -1)           return -1;                                         /* error on send */          /* wait timeout seconds for a response, restart if from wrong server */        while (((nbytes = u_recvfromtimed(requestfd, reply, replen,                                             &senderinfo, timeout)) >= 0) &&               (u_comparehost(&senderinfo, server, serverport) == 0)) ;        if (nbytes >= 0)           break;        retries++;    }    if (retries >= maxretries) {       errno = ETIME;       return -1;    }    return (int)nbytes; } 

With the request-reply with timeouts and retries of Program 20.8, the server may execute the same client request multiple times, with multiple repeats being reflected in the logs produced by the server of Program 20.3. Sometimes reexecution of a request produces invalid results, for example, in banking when a client request to credit an account should not be performed multiple times. On the other hand, a client request for information from a static database can be repeated without ill effect. Operations that can be performed multiple times with the same effect are called idempotent operations . The next section introduces a strategy for handling nonidempotent operations.

Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net