Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

recvn.c

Go to the documentation of this file.
00001 /************************************************************************
00002 * File: recvn.c
00003 * Purpose: Provides a C file for the recvn function from
00004 * the book Advanced Programming in the UNIX Environment  by Richard Stevens.
00005 * The recvn function calls the recv function repeatedly until n bytes
00006 * have been recv from the file descriptor.
00007 * It uses select and FIONREAD checks ahead of time to guarantee that even
00008 * if the socket is blocking the timeout will be enforced.
00009 * To retry a socket to for the data missed during past timeouts the
00010 * application should pass recvn the same buffer and address of a variable
00011 * storing the number of bytes read on previous attempts.
00012 *************************************************************************/
00013 
00014 /* This is neccessary to avoid muliple definitions of fd_set, etc when both
00015 * RPC via PCNFS and Windows Sockets are to be available. */
00016 #ifdef USE_PCNFS
00017 #undef USE_PCNFS
00018 #endif
00019 
00020 #include "rcs_defs.hh"          /* _Windows */
00021 
00022 #include "recvn.h"              /* recvn(int, void *, int, double) */
00023 #ifndef UNDER_CE
00024 #include <stddef.h>             /* size_t */
00025 #include <errno.h>              /* errno */
00026 #endif
00027 #include <string.h>             /* strerror() */
00028 #include <math.h>               /* modf() */
00029 #include "rcs_prnt.hh"          /* rcs_print_error() */
00030 #include "sokintrf.h"           /* dl_select(), dl_recv(), dl_WSAGetLastError() */
00031                                 /* struct timeval, fd_set */
00032 
00033 #include "_timer.h"             /* etime(), esleep() */
00034 
00035 int recvn_timedout = 0;
00036 int print_recvn_timeout_errors = 1;
00037 
00038 /* Read "n" bytes from a descriptor. */
00039 int RCS_EXPORT
00040 recvn (int fd, void *vptr, int n, int flags, double _timeout,
00041        int *bytes_read_ptr)
00042 {
00043   int nleft, nrecv;
00044   int select_ret;
00045   char *ptr;
00046   double start_time, current_time;
00047   struct timeval timeout_tv;
00048   fd_set recv_fd_set;
00049   int bytes_ready;
00050   int bytes_to_read;
00051   if (etime_disabled)
00052     {
00053       _timeout = -1.0;
00054     }
00055 
00056   bytes_ready = bytes_to_read = 0;
00057   timeout_tv.tv_sec = (long) _timeout;
00058   timeout_tv.tv_usec = (long) (_timeout * 1000000.0);
00059   if (timeout_tv.tv_usec >= 1000000)
00060     {
00061       timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000;
00062     }
00063   FD_ZERO (&recv_fd_set);
00064   RCS_FD_SET (fd, &recv_fd_set);
00065 
00066   recvn_timedout = 0;
00067   ptr = (char *) vptr;
00068   nleft = n;
00069   if (NULL != bytes_read_ptr)
00070     {
00071       if (*bytes_read_ptr >= n)
00072         {
00073           rcs_print_error
00074             ("recvn: Invalid parameter -- (*bytes_read_ptr = %d) must be less than (n = %d).\n",
00075              *bytes_read_ptr, n);
00076           return -1;
00077         }
00078       if (*bytes_read_ptr < 0)
00079         {
00080           rcs_print_error
00081             ("recvn: Invalid parameter -- (*bytes_read_ptr = %d) must be greater than or equal to zero.\n");
00082           return -1;
00083         }
00084       ptr += *bytes_read_ptr;
00085       nleft -= *bytes_read_ptr;
00086     }
00087 
00088   start_time = etime ();
00089   while (nleft > 0)
00090     {
00091       if (_timeout > 0.0)
00092         {
00093           switch (select_ret =
00094                   dl_select (fd + 1, &recv_fd_set, (fd_set *) NULL,
00095                              (fd_set *) NULL, &timeout_tv))
00096             {
00097             case -1:
00098 #if !defined(_Windows) || defined(gnuwin32)
00099               rcs_print_error ("Error in select: %d -> %s\n", errno,
00100                                strerror (errno));
00101 #else
00102               rcs_print_error ("Error in select: %d\n",
00103                                dl_WSAGetLastError ());
00104 #endif
00105               if (NULL == bytes_read_ptr)
00106                 {
00107                   rcs_print_error
00108                     ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
00109                      fd, vptr, n, flags, _timeout);
00110                 }
00111               else
00112                 {
00113                   rcs_print_error
00114                     ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
00115                      fd, vptr, n, flags, _timeout, *bytes_read_ptr);
00116                 }
00117               return -1;
00118 
00119             case 0:
00120               if (print_recvn_timeout_errors)
00121                 {
00122                   rcs_print_error ("Recv timed out.\n");
00123                   if (NULL == bytes_read_ptr)
00124                     {
00125                       rcs_print_error
00126                         ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
00127                          fd, vptr, n, flags, _timeout);
00128                     }
00129                   else
00130                     {
00131                       rcs_print_error
00132                         ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
00133                          fd, vptr, n, flags, _timeout, *bytes_read_ptr);
00134                     }
00135                 }
00136               recvn_timedout = 1;
00137               if (NULL != bytes_read_ptr)
00138                 {
00139                   *bytes_read_ptr = (n - nleft);
00140                 }
00141               return -1;
00142 
00143             default:
00144               break;
00145             }
00146           bytes_ready = 0;
00147 #if defined(WIN32) && !defined(gnuwin32)
00148           dl_ioctlsocket (fd, FIONREAD, &bytes_ready);
00149 #else
00150 #ifndef VXWORKS
00151 #ifdef MSDOS
00152           bytes_ready = nleft;
00153 #else
00154           ioctl (fd, FIONREAD, (caddr_t) & bytes_ready);
00155 #endif
00156 #else
00157           ioctl (fd, FIONREAD, (int) &bytes_ready);
00158 #endif
00159 #endif
00160           bytes_to_read = (nleft <= bytes_ready) ? nleft : bytes_ready;
00161         }
00162       else
00163         {
00164           bytes_to_read = nleft;
00165         }
00166       nrecv = 0;
00167       if (bytes_to_read > 0)
00168         {
00169           if ((nrecv = dl_recv (fd, ptr, bytes_to_read, flags)) == -1)
00170             {
00171               if (
00172 #if !defined(_WINDOWS) || defined(gnuwin32)
00173 #ifdef MSDOS
00174                    (EWOULDBLOCK == (tk_geterrno (fd)))
00175 #else
00176                    (errno == EWOULDBLOCK)
00177 #endif
00178 #else
00179                    (WSAEWOULDBLOCK == (dl_WSAGetLastError ()))
00180 #endif
00181                 )
00182                 {
00183                   if (fabs (_timeout) < 1e-6)
00184                     {
00185                       recvn_timedout = 1;
00186                       if (NULL != bytes_read_ptr)
00187                         {
00188                           *bytes_read_ptr = (n - nleft);
00189                         }
00190                       return -1;
00191                     }
00192                 }
00193               else
00194                 {
00195 #ifndef UNDER_CE
00196 #ifdef WIN32
00197                   rcs_print_sys_error (WSAGETLASTERROR_ERROR_SOURCE,
00198                                        "recv error:");
00199 #else
00200                   rcs_print_error ("Recv error: %d = %s\n", errno,
00201                                    strerror (errno));
00202 #endif
00203 #endif
00204                   if (NULL == bytes_read_ptr)
00205                     {
00206                       rcs_print_error
00207                         ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
00208                          fd, vptr, n, flags, _timeout);
00209                     }
00210                   else
00211                     {
00212                       rcs_print_error
00213                         ("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
00214                          fd, vptr, n, flags, _timeout, *bytes_read_ptr);
00215                     }
00216                   if (NULL != bytes_read_ptr)
00217                     {
00218                       *bytes_read_ptr = (n - nleft);
00219                     }
00220                   return (-1);  /* error, return < 0 */
00221                 }
00222               nrecv = 0;
00223             }
00224           else if (nrecv == 0)
00225             {
00226               rcs_print_error ("recvn: Premature EOF recieved.\n");
00227               return (-2);
00228             }
00229         }
00230       nleft -= nrecv;
00231       ptr += nrecv;
00232       if (nleft > 0 && _timeout > 0.0)
00233         {
00234           esleep (0.001);
00235           current_time = etime ();
00236           if (current_time - start_time > _timeout)
00237             {
00238               rcs_print_error ("Recv timed out.\n");
00239               recvn_timedout = 1;
00240               if (NULL != bytes_read_ptr)
00241                 {
00242                   *bytes_read_ptr = (n - nleft);
00243                 }
00244               return (-1);
00245             }
00246         }
00247     }
00248   rcs_print_debug (PRINT_SOCKET_READ_SIZE, "read %d bytes from %d\n", n, fd);
00249   if (NULL != bytes_read_ptr)
00250     {
00251       *bytes_read_ptr = (n - nleft);
00252     }
00253   return (n - nleft);           /* return >= 0 */
00254 }

Generated on Sun Dec 2 15:56:51 2001 for rcslib by doxygen1.2.11.1 written by Dimitri van Heesch, © 1997-2001