/******************************************************************** * Description: sendn.c * Provides a C file for the sendn function from the book Advanced * Programming in the UNIX Environment by Richard Stevens. * The sendn function calls the send function repeatedly until n bytes * have been written to the file descriptor. * * Derived from a work by Fred Proctor & Will Shackleford * * Author: * License: LGPL Version 2 * System: Linux * * Copyright (c) 2004 All rights reserved. * * Last change: ********************************************************************/ #include /* strerror */ #include /* memset() */ #include /* errno */ #include /* fabs() */ #include /* send(), recv(), socket(), accept(), bind(), listen() */ #include /* struct timeval */ #include "sendn.h" /* sendn() */ #include "rcs_print.hh" /* rcs_print_error() */ #include "_timer.h" /* etime(), esleep() */ int sendn_timedout = 0; int print_sendn_timeout_errors = 1; /* Write "n" bytes to a descriptor. */ int sendn(int fd, const void *vptr, int n, int _flags, double _timeout) { int nleft; long nwritten; int select_ret; double start_time; char *ptr; struct timeval timeout_tv; fd_set send_fd_set; timeout_tv.tv_sec = (long) _timeout; timeout_tv.tv_usec = (long) (_timeout * 1000000.0); if (timeout_tv.tv_usec >= 1000000) { timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000; } FD_ZERO(&send_fd_set); FD_SET(fd, &send_fd_set); ptr = (char *) vptr; /* can't do pointer arithmetic on void* */ nleft = n; start_time = etime(); while (nleft > 0) { if (fabs(_timeout) > 1E-6) { if (_timeout > 0) { double timeleft; timeleft = start_time + _timeout - etime(); if (timeleft <= 0.0) { if (print_sendn_timeout_errors) { rcs_print_error ("sendn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) timed out.\n", fd, vptr, n, _flags, _timeout); } sendn_timedout = 1; return -1; } timeout_tv.tv_sec = (long) timeleft; timeout_tv.tv_usec = (long) (timeleft * 1000000.0); if (timeout_tv.tv_usec >= 1000000) { timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000; } select_ret = select(fd + 1, (fd_set *) NULL, &send_fd_set, (fd_set *) NULL, &timeout_tv); } else { select_ret = select(fd + 1, (fd_set *) NULL, &send_fd_set, (fd_set *) NULL, NULL); } switch (select_ret) { case -1: rcs_print_error("Error in select: %d -> %s\n", errno, strerror(errno)); rcs_print_error ("sendn(fd=%d, vptr=%p, int n=%d, int _flags=%d, double _timeout=%f) failed.\n", fd, vptr, n, _flags, _timeout); return -1; case 0: rcs_print_error ("sendn(fd=%d, vptr=%p, int n=%d, int _flags=%d, double _timeout=%f) timed out.\n", fd, vptr, n, _flags, _timeout); return -1; default: break; } } if ((nwritten = send(fd, ptr, nleft, _flags)) == -1) { rcs_print_error("Send error: %d = %s\n", errno, strerror(errno)); return (-1); /* error */ } nleft -= nwritten; ptr += nwritten; if (nleft > 0 && _timeout > 0.0) { double duration; duration = etime() - start_time; if (duration > _timeout) { rcs_print_error("sendn: timed out after %f seconds.\n", duration); return (-1); } esleep(0.001); } } rcs_print_debug(PRINT_SOCKET_WRITE_SIZE, "wrote %d bytes to %d\n", n, fd); return (n); }