The test exposes problem described in #889.

Signed-off-by: Justin Cinkelj <[email protected]>
---
 modules/tests/Makefile      |   3 +-
 tests/tst-tcp-port-reuse.cc | 202 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+), 1 deletion(-)
 create mode 100644 tests/tst-tcp-port-reuse.cc

diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index 5bd0590..4baf172 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -86,7 +86,8 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so 
tst-bsd-evh.so \
        tst-pthread-setcancelstate.so tst-syscall.so tst-pin.so tst-run.so \
        tst-ifaddrs.so tst-pthread-affinity-inherit.so tst-sem-timed-wait.so \
        tst-ttyname.so tst-pthread-barrier.so tst-feexcept.so tst-math.so \
-       tst-sigaltstack.so tst-fread.so tst-tcp-cork.so tst-tcp-v6.so
+       tst-sigaltstack.so tst-fread.so tst-tcp-cork.so tst-tcp-v6.so \
+       tst-tcp-port-reuse.so
 
 #      libstatic-thread-variable.so tst-static-thread-variable.so \
 
diff --git a/tests/tst-tcp-port-reuse.cc b/tests/tst-tcp-port-reuse.cc
new file mode 100644
index 0000000..1d6fd8e
--- /dev/null
+++ b/tests/tst-tcp-port-reuse.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 ScyllaDB
+ * Copyright (C) 2017 XLAB d.o.o.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+/*
+ * This is a test for the tcp port reuse.  ...
+ *
+ * To compile on Linux, use: g++ -g -pthread -std=c++11 
tests/tst-tcp-port-reuse.cc
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <assert.h>
+
+#include <thread>
+#include <chrono>
+
+#define DPRINTF(fmt, ...) { if(0) { printf("%s:%d %s: " fmt, __FILE__, 
__LINE__, __FUNCTION__, ##__VA_ARGS__); } }
+
+// server() opens a listening TCP server on port 1234, accepts one connection,
+// and measures how much time it takes to read all the data from the client.
+static constexpr short LISTEN_TCP_PORT = 1234;
+// client will send data from port 5000.
+static constexpr short CLIENT_TCP_PORT = 5000;
+// how may times to repeat connect/write/read/close
+static constexpr int NUM_ITER = 50;
+// delay in us between each iteration
+static constexpr int DELAY_ITER = 0;
+static constexpr int BUF_SIZE = 4096;
+static constexpr int CLIENT_SEND_SIZE = 100;
+static constexpr int SERVER_SEND_SIZE = 300;
+
+#define CONF_LISTEN_BACKLOG 10
+
+static int server(void)
+{
+    int duration;
+    int num_exchanges = 0;
+    int listenfd;
+    struct sockaddr_in sa;
+
+    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        perror("open listening socket");
+        return 1;
+    };
+    int opt=1;
+    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+    bzero(&sa, sizeof(sa));
+    sa.sin_family = AF_INET;
+    sa.sin_addr.s_addr = INADDR_ANY;
+    sa.sin_port = htons(LISTEN_TCP_PORT);
+    if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa))<0) {
+        perror("bind listen_socket");
+        abort();
+    }
+    if (listen(listenfd, CONF_LISTEN_BACKLOG)<0) {
+        perror("listen");
+        abort();
+    }
+    DPRINTF("listenfd=%d\n", listenfd);
+
+    char *buf = (char*) calloc(BUF_SIZE, 1);
+    for (int ii=0; ii<NUM_ITER; ii++) {
+        int fd = accept(listenfd, NULL, NULL);
+        if (fd < 0) {
+            perror("accept failed");
+            close(listenfd);
+            return 1;
+        }
+        DPRINTF("accepted conn fd=%d\n", fd);
+        // read() the expected 200 bytes. Measure how much time it takes 
between
+        // accepting the connection and reading until getting all those bytes, 
and
+        // return the number of milliseconds.
+        auto before = std::chrono::high_resolution_clock::now();
+
+        int size = 0;
+        int r;
+        while (size < CLIENT_SEND_SIZE && (r = read(fd, buf, BUF_SIZE)) > 0) {
+            size += r;
+            DPRINTF("read %d bytes\n", r);
+        }
+        assert(size == CLIENT_SEND_SIZE); // we should have read everything
+
+        int wr = write(fd, buf, SERVER_SEND_SIZE);
+        assert(wr == SERVER_SEND_SIZE); // smaller than socket buffer, so 
shouldn't fail...
+        DPRINTF("write %d bytes\n", wr);
+
+        if (shutdown(fd, SHUT_RDWR)<0) {
+            perror("shutdown server socket");
+            abort();
+        }
+        if (close(fd)<0) {
+            perror("close server socket");
+            abort();
+        }
+        num_exchanges++;
+        auto after = std::chrono::high_resolution_clock::now();
+        duration = std::chrono::duration_cast<std::chrono::microseconds>(
+                after-before).count();
+        printf("iter %d/%d done in %d [us].\n", ii, NUM_ITER, duration);
+    }
+    close(listenfd);
+    return num_exchanges;
+}
+
+static int client()
+{
+    int ii;
+    int sock = -1;
+    struct sockaddr_in sa;
+
+    char *buf = (char*) calloc(BUF_SIZE, 1);
+    for (ii=0; ii<NUM_ITER; ii++) {
+        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+            perror("open socket");
+            return 1;
+        };
+        int opt=1;
+        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+        bzero(&sa, sizeof(sa));
+        sa.sin_family = AF_INET;
+        sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+        sa.sin_port = htons(CLIENT_TCP_PORT);
+        if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))<0) {
+            perror("bind client socket");
+            abort();
+        }
+        DPRINTF("binded sock=%d\n", sock);
+
+        bzero(&sa, sizeof(sa));
+        sa.sin_family = AF_INET;
+        sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+        sa.sin_port = htons(LISTEN_TCP_PORT);
+        if (connect(sock, (struct sockaddr *)&sa, sizeof(sa))<0) {
+            perror("connect socket");
+            abort();
+        }
+        DPRINTF("connected sock=%d\n", sock);
+
+        bzero(buf, BUF_SIZE);
+        int wr = write(sock, buf, CLIENT_SEND_SIZE);
+        assert(wr == CLIENT_SEND_SIZE); // smaller than socket buffer, so 
shouldn't fail...
+        DPRINTF("write %d bytes\n", wr);
+
+        int size = 0;
+        int r;
+        while (size < SERVER_SEND_SIZE && (r = read(sock, buf, BUF_SIZE)) > 0) 
{
+            size += r;
+            DPRINTF("read %d bytes\n", r);
+        }
+        assert(size == SERVER_SEND_SIZE); // we should have read everything
+
+        //usleep(500000);
+        if (shutdown(sock, SHUT_RDWR)<0) {
+            perror("shutdown client socket");
+            abort();
+        }
+        if (close(sock)<0) {
+            perror("close client socket");
+            abort();
+        }
+        sock = -1;
+        usleep(DELAY_ITER);
+    }
+    free(buf);
+    return 0;
+}
+
+static bool test()
+{
+    int srv_result;
+    std::thread t([&srv_result] { srv_result = server(); });
+    sleep(1);
+    int clnt_result = client();
+    t.join();
+    printf("test rounds: %d, DELAY_ITER=%d us\n", srv_result, DELAY_ITER);
+    return srv_result == NUM_ITER;
+}
+
+static int tests = 0, fails = 0;
+static void report(bool ok, const char* msg)
+{
+    ++tests;
+    fails += !ok;
+    printf("%s: %s\n", (ok ? "PASS" : "FAIL"), msg);
+}
+
+
+int main(int argc, char **argv)
+{
+    report(test(), "TCP port reuse");
+    printf("SUMMARY: %d tests, %d failures\n", tests, fails);
+    return fails;
+}
-- 
2.9.4

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to