About | Log | Files | Refs
commit e18302e83c243b1166fd1909c9054b1f55fc08f0
parent 6d3b7745145321da1614f9ea8150c1768463f08e
Author: Ben Connors <benconnors@outlook.com>
Date: Wed, 5 Aug 2026 16:11:22 -0400
Fix throughput problem
Diffstat:
| M | src/sctp_echo.c | | | 75 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------- |
1 file changed, 61 insertions(+), 14 deletions(-)
diff --git a/src/sctp_echo.c b/src/sctp_echo.c
@@ -8,15 +8,29 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
+#include <math.h>
+#include <pthread.h>
+#include <errno.h>
+
+uint32_t min(uint32_t a, uint32_t b) {
+ return (a > b) ? b : a;
+}
int done = 0;
+int ready = 0;
+
+const size_t send_buffer_size = 1024;
+
+pthread_mutex_t lock;
int receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, size_t datalen, struct sctp_rcvinfo info, int flags, void *ulp_info) {
- // Read data from SCTP and write to stdout
if (data == NULL) {
// Closed connection
done = 1;
usrsctp_close(sock);
+
+ // The mainthread might be stuck, unlock it so it can exit
+ pthread_mutex_unlock(&lock);
} else {
if (write(fileno(stdout), data, datalen) < 0) {
perror("write");
@@ -26,6 +40,26 @@ int receive_cb(struct socket *sock, union sctp_sockstore addr, void *data, size_
return 1;
}
+int send_cb(struct socket *sock, uint32_t sb_free, void *ulp_info) {
+ // Callback called when the socket has space in its send cue. When this function is called is
+ // rather bizarre:
+ //
+ // 1. Once called, this function is not called again until something has been added to the send
+ // queue;
+ // 2. On the client side, this will be called BEFORE a connection has been established;
+ // 3. If it blocks, at least on the client we will get stuck in the establishment process until
+ // it unblocks; and
+ // 4. On the server this is never called after connection establishment until something has
+ // been added to the send queue.
+ //
+ // The easiest solution here is to do all the sending in the main thread and have this unlock
+ // a mutex that the main thread will try to lock whenever it gets `EAGAIN` from usrsctp.
+
+ pthread_mutex_unlock(&lock);
+
+ return 1;
+}
+
int main(int argc, char **argv) {
if (argc < 2 || !(strncmp(argv[1], "-c", 3) == 0 || strncmp(argv[1], "-s", 3) == 0)) {
fprintf(stderr, "Usage: %s [-c|-s] ...\n", argv[0]);
@@ -35,6 +69,8 @@ int main(int argc, char **argv) {
const bool is_server = strncmp(argv[1], "-s", 3) == 0;
const bool is_client = !is_server;
+ pthread_mutex_init(&lock, NULL);
+
int local_udp_port;
int remote_sctp_port;
int remote_ip;
@@ -77,18 +113,14 @@ int main(int argc, char **argv) {
// This is the local UDP encapsulation port
usrsctp_init(local_udp_port, NULL, NULL);
- // The null fields at the end are for the callback API; we use the data received
- // callback since usrsctp doesn't seem to support using poll/select. Since we
- // only deal with two incoming streams (one usrsctp connection and stdin) we can
- // get away with polling stdin directly and letting usrsctp run the callback for
- // its data
+ // We use the callback API since usrsctp doesn't seem to support using poll/select
sock = usrsctp_socket(
PF_INET,
SOCK_STREAM,
IPPROTO_SCTP,
receive_cb,
- NULL,
- 0,
+ send_cb,
+ send_buffer_size,
NULL
);
@@ -122,7 +154,7 @@ int main(int argc, char **argv) {
// This is the remote address
struct sockaddr_in addr;
- socklen_t addr_len;
+ socklen_t addr_len = sizeof(addr);
if (is_client) {
// Set the remote address
@@ -153,7 +185,7 @@ int main(int argc, char **argv) {
}
if (remote_ip != 0 && (addr.sin_addr.s_addr != remote_ip || addr.sin_port != htons(remote_sctp_port))) {
- fprintf(stderr, "Bad IP/port tried: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+ fprintf(stderr, "Bad IP/port tried: %d %s:%d\n", addr_len, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
usrsctp_close(sock);
} else {
// Valid connection
@@ -167,8 +199,10 @@ int main(int argc, char **argv) {
usrsctp_listen(listen_sock, 0);
}
+ ready = 1;
+
// Mainloop: read from stdin and write to the SCTP socket
- char buff[1024];
+ char buff[send_buffer_size];
int data_read;
while (done == 0) {
@@ -177,9 +211,22 @@ int main(int argc, char **argv) {
if (data_read == 0) {
// EOF
done = 1;
- } else if (usrsctp_sendv(sock, buff, data_read, NULL, 0, NULL, 0, 0, 0) < 0) {
- perror("sendv");
- done = 1;
+ } else {
+ while (usrsctp_sendv(sock, buff, data_read, NULL, 0, NULL, 0, 0, 0) < 0) {
+ // usrsctp has a rather small send buffer, so on e.g. a file transfer we will
+ // run into EAGAIN. Using usrsctp_set_non_blocking(sock, 0) doesn't seem to
+ // have any effect on this.
+ if (errno == EAGAIN) {
+ pthread_mutex_lock(&lock);
+ if (done == 1) {
+ break;
+ }
+ } else {
+ perror("sendv");
+ done = 1;
+ break;
+ }
+ }
}
}
}