From: Urban Wallasch Date: Fri, 25 Oct 2019 14:45:45 +0000 (+0200) Subject: * net: added sendfile_tm() X-Git-Url: https://git.packet-gain.de/?a=commitdiff_plain;h=5ad5ad3e50c0dc6c259c86241c9828e7e5f49212;p=oddbits.git * net: added sendfile_tm() --- diff --git a/net/net.c b/net/net.c index a925ea5..0b048bf 100644 --- a/net/net.c +++ b/net/net.c @@ -22,6 +22,14 @@ #include "net.h" + +/* Size of copy buffer in sendfile_tm(): */ +#define SENDFILE_TM_BUFSIZE 64000 + +/* Retry attempts for zero byte write in sendfile_tm(): */ +#define SENDFILE_TM_RETRY_0 5 + + /* Logging support macros and helper functions: */ #if defined(NET_DBG) && !defined(NET_ERR) @@ -391,3 +399,60 @@ ssize_t sendto_tm(int fd, void *buf, size_t len, int flags, return r; } +/* Copy count bytes from in_fd to out_fd: */ +int64_t sendfile_tm(int out_fd, int in_fd, int64_t count, int timeout) { + int64_t ret, total; + char *buf; + + buf = malloc(SENDFILE_TM_BUFSIZE); + if (buf == NULL) + return EAI_SYSTEM; + + for (total = 0, ret = 0; ret == 0; ) { + int retry_zero_wr; + ssize_t nrd, nwr, n; + n = SENDFILE_TM_BUFSIZE; + if (count >= 0 && n > count - total) + n = count - total; + nrd = read_tm(in_fd, buf, n, timeout); + if (nrd < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + continue; + ret = nrd; + continue; + } + else if (nrd == 0) { /* all done */ + ret = total; + break; /* Avoid infinite loop on empty input! */ + } + retry_zero_wr = SENDFILE_TM_RETRY_0; + nwr = 0; + while (nwr < nrd && ret == 0) { + n = write_tm(out_fd, buf + nwr, nrd - nwr, timeout); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + continue; + ret = n; + continue; + } + else if (n == 0) { + if (retry_zero_wr-- <= 0) { + errno = ENOSPC; + ret = EAI_SYSTEM; + NETLOG_ERR(ret, "sendfile_tm(%d,%d)", out_fd, in_fd); + continue; + } + NETLOG_DBG("sendfile_tm(%d,%d): zero write, retry %d", + out_fd, in_fd, SENDFILE_TM_RETRY_0 - retry_zero_wr); + } + else { + retry_zero_wr = SENDFILE_TM_RETRY_0; + } + nwr += n; + } + total += nwr; + } + free(buf); + return ret; +} + diff --git a/net/net.h b/net/net.h index f1a8454..0a793fe 100644 --- a/net/net.h +++ b/net/net.h @@ -226,6 +226,27 @@ extern ssize_t sendto_tm(int fd, void *buf, size_t len, int flags, #define write_tm(d_,b_,l_,t_) sendto_tm((d_),(b_),(l_),0,NULL,0,(t_)) +/* + * Copy up to count bytes of data from in_fd to out_fd with timeout. + * + * Returns the total number of bytes copied, or a negative value on error. + * + * out_fd - file or socket descriptor to write to + * in_fd - file or socket descriptor to read from + * count - maximum number of bytes to copy + * timeout - timeout in milliseconds + * + * Note: The timeout is applied to each individual internal read or write + * operation. If zero bytes were written, up to 5 attempts to retransmit + * are made. Thus the timeout may add up multiple times. + * + * If count is negative, copying takes place until the end of the input + * stream is hit. If count is zero a single read operation with a count + * of zero is performed. + */ +extern int64_t sendfile_tm(int out_fd, int in_fd, int64_t count, int timeout); + + #ifdef cplusplus } #endif diff --git a/net/net_test.c b/net/net_test.c index 3d764a2..e44200f 100644 --- a/net/net_test.c +++ b/net/net_test.c @@ -19,6 +19,10 @@ #include #include +#include +#include +#include + #include "net.h" #define LOG(...) fprintf(stderr, __VA_ARGS__) @@ -38,10 +42,13 @@ int main(int argc, char *argv[]) { int arg = 1, af = AF_UNSPEC, mode = 't'; char *nod = "localhost"; char *svc = "12345"; + char *fname = NULL; char buf[4000]; if (argc < 2) { - LOG("Usage: %s [4|6] [t|T|u|U [address [service]]]\n", argv[0]); + LOG("Usage:\n"); + LOG("%s [4|6] [t|T|u|U [address [service]]]\n", argv[0]); + LOG("%s [4|6] F [address [service [filename]]]\n", argv[0]); exit(EXIT_FAILURE); } if (*argv[arg] == '4') { @@ -58,6 +65,8 @@ int main(int argc, char *argv[]) { nod = argv[arg++]; if (argv[arg]) svc = argv[arg++]; + if (argv[arg]) + fname = argv[arg++]; switch (mode) { default: @@ -139,6 +148,42 @@ int main(int argc, char *argv[]) { LOG_NET(l, "net_close"); break; + case 'F': + // TCP sendfile server + printf("TCP%s file server (%s)\n", AFTOS(af),fname?fname:"stdin"); + l = net_open_server(nod, svc, SOCK_STREAM, af); + if (l < 0) { + LOG_NET(l, "tcp6_open_server"); + exit(EXIT_FAILURE); + } + while (1) { + c = tcp_accept(l, 5000); + LOG_NET(c, "tcp_accept"); + if (c < 0) { + continue; + } + int in_fd = 0; + if (fname) { + in_fd = open(fname, O_RDONLY); + if (in_fd < 0) { + LOG("open(\"%s\"): %s\n", fname, strerror(errno)); + exit(EXIT_FAILURE); + } + } + ssize_t tot; + tot = sendfile_tm(c, in_fd, -1, 10000); + if (0 <= tot) + LOG("net_sendfile: %zd sent\n", tot); + else + LOG("net_sendfile: %s\n", net_strerror(tot, buf, sizeof buf)); + c = net_close(c); + LOG_NET(c, "net_close"); + if (in_fd == 0) + break; + close(in_fd); + } + break; + case 'u': // UDP generator client printf("UDP%s client\n", AFTOS(af));