sendn.c
1 /******************************************************************** 2 * Description: sendn.c 3 * Provides a C file for the sendn function from the book Advanced 4 * Programming in the UNIX Environment by Richard Stevens. 5 * The sendn function calls the send function repeatedly until n bytes 6 * have been written to the file descriptor. 7 * 8 * Derived from a work by Fred Proctor & Will Shackleford 9 * 10 * Author: 11 * License: LGPL Version 2 12 * System: Linux 13 * 14 * Copyright (c) 2004 All rights reserved. 15 * 16 * Last change: 17 ********************************************************************/ 18 19 #include <string.h> /* strerror */ 20 #include <stdlib.h> /* memset() */ 21 #include <errno.h> /* errno */ 22 #include <math.h> /* fabs() */ 23 #include <sys/socket.h> /* send(), recv(), socket(), accept(), 24 bind(), listen() */ 25 #include <sys/time.h> /* struct timeval */ 26 #include "sendn.h" /* sendn() */ 27 #include "rcs_print.hh" /* rcs_print_error() */ 28 #include "_timer.h" /* etime(), esleep() */ 29 30 int sendn_timedout = 0; 31 int print_sendn_timeout_errors = 1; 32 33 /* Write "n" bytes to a descriptor. */ 34 int sendn(int fd, const void *vptr, int n, int _flags, double _timeout) 35 { 36 int nleft; 37 long nwritten; 38 int select_ret; 39 double start_time; 40 char *ptr; 41 struct timeval timeout_tv; 42 fd_set send_fd_set; 43 44 timeout_tv.tv_sec = (long) _timeout; 45 timeout_tv.tv_usec = (long) (_timeout * 1000000.0); 46 if (timeout_tv.tv_usec >= 1000000) { 47 timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000; 48 } 49 FD_ZERO(&send_fd_set); 50 FD_SET(fd, &send_fd_set); 51 52 ptr = (char *) vptr; /* can't do pointer arithmetic on void* */ 53 nleft = n; 54 start_time = etime(); 55 while (nleft > 0) { 56 if (fabs(_timeout) > 1E-6) { 57 if (_timeout > 0) { 58 double timeleft; 59 timeleft = start_time + _timeout - etime(); 60 if (timeleft <= 0.0) { 61 if (print_sendn_timeout_errors) { 62 rcs_print_error 63 ("sendn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) timed out.\n", 64 fd, vptr, n, _flags, _timeout); 65 } 66 sendn_timedout = 1; 67 return -1; 68 } 69 timeout_tv.tv_sec = (long) timeleft; 70 timeout_tv.tv_usec = (long) (timeleft * 1000000.0); 71 if (timeout_tv.tv_usec >= 1000000) { 72 timeout_tv.tv_usec = timeout_tv.tv_usec % 1000000; 73 } 74 select_ret = 75 select(fd + 1, (fd_set *) NULL, &send_fd_set, 76 (fd_set *) NULL, &timeout_tv); 77 } else { 78 select_ret = 79 select(fd + 1, (fd_set *) NULL, &send_fd_set, 80 (fd_set *) NULL, NULL); 81 } 82 switch (select_ret) { 83 case -1: 84 rcs_print_error("Error in select: %d -> %s\n", errno, 85 strerror(errno)); 86 rcs_print_error 87 ("sendn(fd=%d, vptr=%p, int n=%d, int _flags=%d, double _timeout=%f) failed.\n", 88 fd, vptr, n, _flags, _timeout); 89 return -1; 90 91 case 0: 92 rcs_print_error 93 ("sendn(fd=%d, vptr=%p, int n=%d, int _flags=%d, double _timeout=%f) timed out.\n", 94 fd, vptr, n, _flags, _timeout); 95 return -1; 96 97 default: 98 break; 99 } 100 } 101 if ((nwritten = send(fd, ptr, nleft, _flags)) == -1) { 102 rcs_print_error("Send error: %d = %s\n", errno, strerror(errno)); 103 return (-1); /* error */ 104 } 105 nleft -= nwritten; 106 ptr += nwritten; 107 if (nleft > 0 && _timeout > 0.0) { 108 double duration; 109 duration = etime() - start_time; 110 if (duration > _timeout) { 111 rcs_print_error("sendn: timed out after %f seconds.\n", duration); 112 return (-1); 113 } 114 esleep(0.001); 115 } 116 } 117 rcs_print_debug(PRINT_SOCKET_WRITE_SIZE, "wrote %d bytes to %d\n", n, fd); 118 return (n); 119 }