#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)
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;
+}
+
#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
#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__)
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') {
nod = argv[arg++];
if (argv[arg])
svc = argv[arg++];
+ if (argv[arg])
+ fname = argv[arg++];
switch (mode) {
default:
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));