commit d8b18c7bfa0d75f38bbceefde217c83813473e5f
parent 1c6dfdef1faabdb80161e5490526491e2a02c28c
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Sat, 23 Sep 2017 16:08:28 +0200
optimize binary file transfers: use sendfile(2) syscall if supported
The OS supporting sendfile(2) are (for now): Linux, FreeBSD, DragonFlyBSD.
When the file is empty or has no filesize information (such as block,
character devices etc) fallback to the normal read/write loop in userland.
A platform with no sendfile(2) support always uses this loop.
Optimize the buffer size in the normal read/write loop by using the block size
information, fallback to BUFSIZ.
Set socket options TCP_CORK (Linux), TCP_NOPUSH (BSDs) and TCP_NODELAY to
optimize packet transfers for large file transfers.
Tested on Linux (glibc and musl), FreeBSD, DragonFlyBSD and OpenBSD.
Signed-off-by: Christoph Lohmann <20h@r-36.net>
Diffstat:
handlr.c | | | 14 | +++----------- |
ind.c | | | 68 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
ind.h | | | 1 | + |
3 files changed, 72 insertions(+), 11 deletions(-)
diff --git a/handlr.c b/handlr.c
@@ -104,8 +104,7 @@ void
handlebin(int sock, char *file, char *port, char *base, char *args,
char *sear, char *ohost)
{
- char sendb[1024];
- int len, fd, sent;
+ int fd;
USED(port);
USED(base);
@@ -115,15 +114,8 @@ handlebin(int sock, char *file, char *port, char *base, char *args,
fd = open(file, O_RDONLY);
if(fd >= 0) {
- while((len = read(fd, sendb, sizeof(sendb))) > 0) {
- while(len > 0) {
- if ((sent = send(sock, sendb, len, 0)) < 0) {
- close(fd);
- return;
- }
- len -= sent;
- }
- }
+ if(xsendfile(fd, sock) < 0)
+ perror("sendfile");
close(fd);
}
}
diff --git a/ind.c b/ind.c
@@ -12,9 +12,19 @@
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
+/* for sendfile(2) */
+#ifdef __linux__
+#include <sys/sendfile.h>
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
#include "ind.h"
#include "handlr.h"
@@ -42,6 +52,64 @@ filetype type[] = {
{nil, nil, nil},
};
+int
+xsendfile(int fd, int sock)
+{
+ struct stat st;
+ char *sendb;
+ size_t bufsiz = BUFSIZ, count = 0;
+ int len, sent, optval;
+
+#ifdef TCP_CORK
+ optval = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_CORK, &optval, sizeof(int));
+#endif
+
+#ifdef TCP_NOPUSH
+ optval = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_NOPUSH, &optval, sizeof(int));
+#endif
+
+#ifdef TCP_NODELAY
+ optval = 0;
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
+#endif
+
+ if(fstat(fd, &st) >= 0) {
+ if((bufsiz = st.st_blksize) < BUFSIZ)
+ bufsiz = BUFSIZ;
+ count = st.st_size;
+ }
+
+#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__DragonFly__)
+ count = 0;
+#endif
+
+ if (count == 0) {
+ sendb = xmalloc(bufsiz);
+ while((len = read(fd, sendb, bufsiz)) > 0) {
+ while(len > 0) {
+ if ((sent = send(sock, sendb, len, 0)) < 0) {
+ close(fd);
+ free(sendb);
+ return -1;
+ }
+ len -= sent;
+ }
+ }
+ free(sendb);
+ return 0;
+ }
+
+#ifdef __linux__
+ return sendfile(sock, fd, NULL, count);
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ return sendfile(fd, sock, 0, count, NULL, NULL, 0);
+#endif
+ return -1;
+}
+
void *
xcalloc(size_t nmemb, size_t size)
{
diff --git a/ind.h b/ind.h
@@ -35,6 +35,7 @@ void *xcalloc(size_t, size_t);
void *xmalloc(size_t);
void *xrealloc(void *, size_t);
char *xstrdup(const char *str);
+int xsendfile(int, int);
Indexs *scanfile(char *fname);
Elems *getadv(char *str);
int printelem(int fd, Elems *el, char *addr, char *port);