* net: added sendfile_tm()
authorUrban Wallasch <urban.wallasch@freenet.de>
Fri, 25 Oct 2019 14:45:45 +0000 (16:45 +0200)
committerUrban Wallasch <urban.wallasch@freenet.de>
Fri, 25 Oct 2019 14:45:45 +0000 (16:45 +0200)
net/net.c
net/net.h
net/net_test.c

index a925ea58522e5b0fd15de6a2883512441911bbba..0b048bfa0c2688dafda403a1e19c401293d4c12a 100644 (file)
--- a/net/net.c
+++ b/net/net.c
 
 #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;
+}
+
index f1a84543a438f0da0a122fc0279814ab1a32f7eb..0a793fe1924a8305fde066f0be2489ee246735eb 100644 (file)
--- 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  - <int> file or socket descriptor to write to
+ * in_fd   - <int> file or socket descriptor to read from
+ * count   - <int64_t> maximum number of bytes to copy
+ * timeout - <int> 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
index 3d764a2fa346a63492bee6ff449aef9da0c6ff4d..e44200fa3dc2f352f8b3681f42a5bfc65de7c30a 100644 (file)
 #include <unistd.h>
 #include <netinet/in.h>
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #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));