/ src / libnml / buffer / recvn.c
recvn.c
  1  /********************************************************************
  2  * Description: recvn.c
  3  *   Provides a C file for the recvn function from the book Advanced
  4  *   Programming in the UNIX Environment by Richard Stevens.
  5  *   The recvn function is called repeatedly until n bytes have been
  6  *   received from the file descriptor. It uses select and FIONREAD
  7  *   checks ahead of time to guarantee that even if the socket is
  8  *   blocking the timeout will be enforced. To retry a socket to for
  9  *   the data missed during past timeouts the application should pass
 10  *   recvn the same buffer and address of a variable storing the number
 11  *   of bytes read on previous attempts.
 12  *
 13  *   Derived from a work by Fred Proctor & Will Shackleford
 14  *
 15  * Author:
 16  * License: LGPL Version 2
 17  * System: Linux
 18  *    
 19  * Copyright (c) 2004 All rights reserved.
 20  *
 21  * Last change: 
 22  ********************************************************************/
 23  
 24  #include "recvn.h"		/* recvn(int, void *, int, double) */
 25  #include <stddef.h>		/* size_t */
 26  #include <errno.h>		/* errno */
 27  #include <sys/types.h>		/* typedef fd_set, FD_ZERO, FD_SET */
 28  #include <sys/ioctl.h>		/* FIONREAD */
 29  #include <sys/socket.h>		/* recv() */
 30  #include <sys/time.h>		/* struct timeval */
 31  #include <stdlib.h>		/* malloc(), free() */
 32  #include <string.h>		/* strerror() */
 33  #include <math.h>		/* modf() */
 34  #include "rcs_print.hh"		/* rcs_print_error() */
 35  #include "_timer.h"		/* etime(), esleep() */
 36  
 37  int recvn_timedout = 0;
 38  int print_recvn_timeout_errors = 1;
 39  
 40  /* Read "n" bytes from a descriptor. */
 41  int recvn(int fd, void *vptr, int n, int flags, double _timeout,
 42      int *bytes_read_ptr)
 43  {
 44      int nleft, nrecv;
 45      char *ptr;
 46      double start_time;
 47      struct timeval timeout_tv;
 48      fd_set recv_fd_set;
 49      int bytes_ready;
 50      int bytes_to_read;
 51      if (etime_disabled) {
 52  	_timeout = -1.0;
 53      }
 54  
 55      bytes_ready = 0;
 56      timeout_tv.tv_sec = (long) _timeout;
 57      timeout_tv.tv_usec = (long) (_timeout * 1000000.0);
 58      if (timeout_tv.tv_usec >= 1000000) {
 59  	timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000;
 60      }
 61      FD_ZERO(&recv_fd_set);
 62      FD_SET(fd, &recv_fd_set);
 63  
 64      recvn_timedout = 0;
 65      ptr = (char *) vptr;
 66      nleft = n;
 67      if (NULL != bytes_read_ptr) {
 68  	if (*bytes_read_ptr >= n) {
 69  	    rcs_print_error
 70  		("recvn: Invalid parameter -- (*bytes_read_ptr = %d) must be less than (n = %d).\n",
 71  		*bytes_read_ptr, n);
 72  	    return -1;
 73  	}
 74  	if (*bytes_read_ptr < 0) {
 75  	    rcs_print_error
 76  		("recvn: Invalid parameter -- (*bytes_read_ptr = %d) must be greater than or equal to zero.\n",
 77                  *bytes_read_ptr);
 78  	    return -1;
 79  	}
 80  	ptr += *bytes_read_ptr;
 81  	nleft -= *bytes_read_ptr;
 82      }
 83  
 84      start_time = etime();
 85      while (nleft > 0) {
 86  	if (_timeout > 0.0) {
 87              double timeleft;
 88  	    timeleft = start_time + _timeout - etime();
 89  	    if (timeleft <= 0.0) {
 90  		if (print_recvn_timeout_errors) {
 91  		    rcs_print_error("Recv timed out.\n");
 92  		    if (NULL == bytes_read_ptr) {
 93  			rcs_print_error
 94  			    ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
 95  			    fd, vptr, n, flags, _timeout);
 96  		    } else {
 97  			rcs_print_error
 98  			    ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
 99  			    fd, vptr, n, flags, _timeout, *bytes_read_ptr);
100  		    }
101  		}
102  		recvn_timedout = 1;
103  		if (NULL != bytes_read_ptr) {
104  		    *bytes_read_ptr = (n - nleft);
105  		}
106  		return -1;
107  	    }
108  	    timeout_tv.tv_sec = (long) timeleft;
109  	    timeout_tv.tv_usec = (long) (timeleft * 1000000.0);
110  	    if (timeout_tv.tv_usec >= 1000000) {
111  		timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000;
112  	    }
113  	    switch (select(fd + 1, &recv_fd_set, (fd_set *) NULL,
114  		    (fd_set *) NULL, &timeout_tv)) {
115  	    case -1:
116  		rcs_print_error("Error in select: %d -> %s\n", errno,
117  		    strerror(errno));
118  		if (NULL == bytes_read_ptr) {
119  		    rcs_print_error
120  			("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
121  			fd, vptr, n, flags, _timeout);
122  		} else {
123  		    rcs_print_error
124  			("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
125  			fd, vptr, n, flags, _timeout, *bytes_read_ptr);
126  		}
127  		return -1;
128  
129  	    case 0:
130  		if (print_recvn_timeout_errors) {
131  		    rcs_print_error("Recv timed out.\n");
132  		    if (NULL == bytes_read_ptr) {
133  			rcs_print_error
134  			    ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
135  			    fd, vptr, n, flags, _timeout);
136  		    } else {
137  			rcs_print_error
138  			    ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
139  			    fd, vptr, n, flags, _timeout, *bytes_read_ptr);
140  		    }
141  		}
142  		recvn_timedout = 1;
143  		if (NULL != bytes_read_ptr) {
144  		    *bytes_read_ptr = (n - nleft);
145  		}
146  		return -1;
147  
148  	    default:
149  		break;
150  	    }
151  	    bytes_ready = 0;
152  
153  	    ioctl(fd, FIONREAD, (caddr_t) & bytes_ready);
154  
155  	    bytes_to_read = (nleft <= bytes_ready) ? nleft : bytes_ready;
156  	} else {
157  	    bytes_to_read = nleft;
158  	}
159  	nrecv = 0;
160  	if (bytes_to_read > 0) {
161  	    if ((nrecv = recv(fd, ptr, bytes_to_read, flags)) == -1) {
162  		if (errno == EWOULDBLOCK) {
163  		    if (fabs(_timeout) < 1e-6) {
164  			recvn_timedout = 1;
165  			if (NULL != bytes_read_ptr) {
166  			    *bytes_read_ptr = (n - nleft);
167  			}
168  			return -1;
169  		    }
170  		} else {
171  		    rcs_print_error("Recv error: %d = %s\n", errno,
172  			strerror(errno));
173  		    if (NULL == bytes_read_ptr) {
174  			rcs_print_error
175  			    ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
176  			    fd, vptr, n, flags, _timeout);
177  		    } else {
178  			rcs_print_error
179  			    ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
180  			    fd, vptr, n, flags, _timeout, *bytes_read_ptr);
181  		    }
182  		    if (NULL != bytes_read_ptr) {
183  			*bytes_read_ptr = (n - nleft);
184  		    }
185  		    return (-1);	/* error, return < 0 */
186  		}
187  		nrecv = 0;
188  	    } else if (nrecv == 0) {
189  		rcs_print_error("recvn: Premature EOF received.\n");
190  		return (-2);
191  	    }
192  	}
193  	nleft -= nrecv;
194  	ptr += nrecv;
195  	if (nleft > 0 && _timeout > 0.0) {
196  	    esleep(0.001);
197  	    if (etime() - start_time > _timeout) {
198  		rcs_print_error("Recv timed out.\n");
199  		recvn_timedout = 1;
200  		if (NULL != bytes_read_ptr) {
201  		    *bytes_read_ptr = (n - nleft);
202  		}
203  		return (-1);
204  	    }
205  	}
206      }
207      rcs_print_debug(PRINT_SOCKET_READ_SIZE, "read %d bytes from %d\n", n, fd);
208      if (NULL != bytes_read_ptr) {
209  	*bytes_read_ptr = (n - nleft);
210      }
211      return (n - nleft);		/* return >= 0 */
212  }