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.