From 0075014c4075d10d2cc0fba2bd2052cde4534272 Mon Sep 17 00:00:00 2001 From: Heikki Hannikainen Date: Sun, 23 Oct 2022 13:43:18 +0300 Subject: [PATCH] sctp: Buffer outgoing data, similar to TCP Instead of sending one APRS-IS line per SCTP message, combine multiple messages in a single SCTP message. This reduces the amount of system calls a lot and improves eventual DTLS performance. --- src/sctp.c | 56 +++++++++++++++++++++++++++++++++------------------- src/worker.c | 4 ++-- src/worker.h | 1 + 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/sctp.c b/src/sctp.c index df3b4ce..db14e7e 100644 --- a/src/sctp.c +++ b/src/sctp.c @@ -245,8 +245,6 @@ int sctp_readable(struct worker_t *self, struct client_t *c) } //hlog_packet(LOG_DEBUG, iov.iov_base, e, "sctp_readable: got data: "); - c->ibuf[e++] = '\r'; - c->ibuf[e++] = '\n'; return client_postread(self, c, e); } @@ -268,27 +266,45 @@ int sctp_writable(struct worker_t *self, struct client_t *c) int sctp_client_write(struct worker_t *self, struct client_t *c, char *p, int len) { - //hlog_packet(LOG_DEBUG, p, len, "client_write_sctp %d bytes: ", len); + //hlog_packet(LOG_DEBUG, p, len, "%s/%s: client_write_sctp %d bytes: ", c->addr_rem, c->username, len); - if (len == 0) - return 0; - - clientaccount_add( c, IPPROTO_SCTP, 0, 0, len, 0, 0, 0); - - int i = send(c->fd, p, len-2, 0); - - if (i < 0) { - hlog(LOG_ERR, "SCTP transmit error to fd %d / %s: %s", - c->fd, c->addr_rem, strerror(errno)); - } else if (i != len -2) { - hlog(LOG_ERR, "SCTP transmit incomplete to fd %d / %s: wrote %d of %d bytes, errno: %s", - c->fd, c->addr_rem, i, len-2, strerror(errno)); - } else { - //hlog(LOG_DEBUG, "SCTP transmit ok to %s: %d bytes", c->addr_rem, i); - c->obuf_wtime = tick; + if (len > 0) { + c->obuf_writes++; + if (client_buffer_outgoing_data(self, c, p, len) == -12) + return -12; + clientaccount_add( c, IPPROTO_SCTP, 0, 0, len, 0, 0, 0); } - return i; + /* Is it over the flush size ? */ + if (c->obuf_end > c->obuf_flushsize || ((len == 0) && (c->obuf_end > c->obuf_start))) { + int to_send = c->obuf_end - c->obuf_start; + int i = send(c->fd, c->obuf + c->obuf_start, to_send, 0); + + if (i < 0) { + hlog(LOG_ERR, "%s/%s: SCTP transmit error to fd %d: %s", + c->addr_rem, c->username, c->fd, strerror(errno)); + client_close(self, c, errno); + return -9; + } else if (i != to_send) { + // Incomplete write with SCTP is not great, as we might not have ordered delivery. + hlog(LOG_ERR, "%s/%s: SCTP transmit incomplete to fd %d: wrote %d of %d bytes, errno: %s", + c->addr_rem, c->username, c->fd, i, to_send, strerror(errno)); + client_close(self, c, errno); + return -9; + } else { + //hlog(LOG_DEBUG, "%s/%s: SCTP transmit ok: %d bytes", c->addr_rem, c->username, i); + } + c->obuf_start += i; + c->obuf_wtime = tick; + } + + /* All done ? */ + if (c->obuf_start >= c->obuf_end) { + hlog(LOG_DEBUG, "%s/%s: client_write obuf empty", c->addr_rem, c->username, c->addr_rem); + c->obuf_start = 0; + c->obuf_end = 0; + } + return len; } diff --git a/src/worker.c b/src/worker.c index d40ec2e..354c9c8 100644 --- a/src/worker.c +++ b/src/worker.c @@ -960,7 +960,7 @@ static char *client_buffer_outgoing_allocate(struct client_t *c, int len) return c->obuf + c->obuf_end; } -static int client_buffer_outgoing_data(struct worker_t *self, struct client_t *c, char *p, int len) +int client_buffer_outgoing_data(struct worker_t *self, struct client_t *c, char *p, int len) { char *buf_tail = client_buffer_outgoing_allocate(c, len); @@ -1662,7 +1662,7 @@ static void send_keepalives(struct worker_t *self) c->obuf_flushsize = flushlevel; } else { /* just fush if there was anything to write */ - if (c->ai_protocol == IPPROTO_TCP) { + if (c->ai_protocol == IPPROTO_TCP || c->ai_protocol == IPPROTO_SCTP) { rc = c->write(self, c, buf, 0); if (rc < -2) continue; // destroyed.. } diff --git a/src/worker.h b/src/worker.h index ee31528..0eddcf6 100644 --- a/src/worker.h +++ b/src/worker.h @@ -491,6 +491,7 @@ extern void pbuf_dump(FILE *fp); extern void pbuf_dupe_dump(FILE *fp); extern int client_postread(struct worker_t *self, struct client_t *c, int r); +extern int client_buffer_outgoing_data(struct worker_t *self, struct client_t *c, char *p, int len); extern int client_printf(struct worker_t *self, struct client_t *c, const char *fmt, ...); extern int client_write(struct worker_t *self, struct client_t *c, char *p, int len);