#include "recvn.h"
#include <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "rcs_print.hh"
#include "_timer.h"
int recvn_timedout = 0;
int print_recvn_timeout_errors = 1;
int recvn(int fd, void *vptr, int n, int flags, double _timeout,
int *bytes_read_ptr)
{
int nleft, nrecv;
char *ptr;
double start_time;
struct timeval timeout_tv;
fd_set recv_fd_set;
int bytes_ready;
int bytes_to_read;
if (etime_disabled) {
_timeout = -1.0;
}
bytes_ready = 0;
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(&recv_fd_set);
FD_SET(fd, &recv_fd_set);
recvn_timedout = 0;
ptr = (char *) vptr;
nleft = n;
if (NULL != bytes_read_ptr) {
if (*bytes_read_ptr >= n) {
rcs_print_error
("recvn: Invalid parameter -- (*bytes_read_ptr = %d) must be less than (n = %d).\n",
*bytes_read_ptr, n);
return -1;
}
if (*bytes_read_ptr < 0) {
rcs_print_error
("recvn: Invalid parameter -- (*bytes_read_ptr = %d) must be greater than or equal to zero.\n",
*bytes_read_ptr);
return -1;
}
ptr += *bytes_read_ptr;
nleft -= *bytes_read_ptr;
}
start_time = etime();
while (nleft > 0) {
if (_timeout > 0.0) {
double timeleft;
timeleft = start_time + _timeout - etime();
if (timeleft <= 0.0) {
if (print_recvn_timeout_errors) {
rcs_print_error("Recv timed out.\n");
if (NULL == bytes_read_ptr) {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
fd, vptr, n, flags, _timeout);
} else {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
fd, vptr, n, flags, _timeout, *bytes_read_ptr);
}
}
recvn_timedout = 1;
if (NULL != bytes_read_ptr) {
*bytes_read_ptr = (n - nleft);
}
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;
}
switch (select(fd + 1, &recv_fd_set, (fd_set *) NULL,
(fd_set *) NULL, &timeout_tv)) {
case -1:
rcs_print_error("Error in select: %d -> %s\n", errno,
strerror(errno));
if (NULL == bytes_read_ptr) {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
fd, vptr, n, flags, _timeout);
} else {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
fd, vptr, n, flags, _timeout, *bytes_read_ptr);
}
return -1;
case 0:
if (print_recvn_timeout_errors) {
rcs_print_error("Recv timed out.\n");
if (NULL == bytes_read_ptr) {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
fd, vptr, n, flags, _timeout);
} else {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
fd, vptr, n, flags, _timeout, *bytes_read_ptr);
}
}
recvn_timedout = 1;
if (NULL != bytes_read_ptr) {
*bytes_read_ptr = (n - nleft);
}
return -1;
default:
break;
}
bytes_ready = 0;
ioctl(fd, FIONREAD, (caddr_t) & bytes_ready);
bytes_to_read = (nleft <= bytes_ready) ? nleft : bytes_ready;
} else {
bytes_to_read = nleft;
}
nrecv = 0;
if (bytes_to_read > 0) {
if ((nrecv = recv(fd, ptr, bytes_to_read, flags)) == -1) {
if (errno == EWOULDBLOCK) {
if (fabs(_timeout) < 1e-6) {
recvn_timedout = 1;
if (NULL != bytes_read_ptr) {
*bytes_read_ptr = (n - nleft);
}
return -1;
}
} else {
rcs_print_error("Recv error: %d = %s\n", errno,
strerror(errno));
if (NULL == bytes_read_ptr) {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f) failed.\n",
fd, vptr, n, flags, _timeout);
} else {
rcs_print_error
("recvn(fd=%d, vptr=%p, int n=%d, int flags=%d, double _timeout=%f,bytes_read=%d) failed.\n",
fd, vptr, n, flags, _timeout, *bytes_read_ptr);
}
if (NULL != bytes_read_ptr) {
*bytes_read_ptr = (n - nleft);
}
return (-1);
}
nrecv = 0;
} else if (nrecv == 0) {
rcs_print_error("recvn: Premature EOF received.\n");
return (-2);
}
}
nleft -= nrecv;
ptr += nrecv;
if (nleft > 0 && _timeout > 0.0) {
esleep(0.001);
if (etime() - start_time > _timeout) {
rcs_print_error("Recv timed out.\n");
recvn_timedout = 1;
if (NULL != bytes_read_ptr) {
*bytes_read_ptr = (n - nleft);
}
return (-1);
}
}
}
rcs_print_debug(PRINT_SOCKET_READ_SIZE, "read %d bytes from %d\n", n, fd);
if (NULL != bytes_read_ptr) {
*bytes_read_ptr = (n - nleft);
}
return (n - nleft);
}