summaryrefslogtreecommitdiff
path: root/src/tools/oping
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/oping')
-rw-r--r--src/tools/oping/CMakeLists.txt28
-rw-r--r--src/tools/oping/oping.c113
-rw-r--r--src/tools/oping/oping_client.c263
-rw-r--r--src/tools/oping/oping_server.c93
4 files changed, 364 insertions, 133 deletions
diff --git a/src/tools/oping/CMakeLists.txt b/src/tools/oping/CMakeLists.txt
deleted file mode 100644
index 31a4f961..00000000
--- a/src/tools/oping/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-
-find_library(LIBM_LIBRARIES m)
-if(NOT LIBM_LIBRARIES)
- message(FATAL_ERROR "libm not found")
-endif()
-
-mark_as_advanced(LIBM_LIBRARIES)
-
-set(SOURCE_FILES
- # Add source files here
- oping.c
- )
-
-add_executable(oping ${SOURCE_FILES})
-
-target_link_libraries(oping LINK_PUBLIC ${LIBM_LIBRARIES} ouroboros-dev)
-
-install(TARGETS oping RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/oping/oping.c b/src/tools/oping/oping.c
index ca685292..763c0d62 100644
--- a/src/tools/oping/oping.c
+++ b/src/tools/oping/oping.c
@@ -1,10 +1,10 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2020
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* Ouroboros ping application
*
- * Dimitri Staessens <dimitri.staessens@ugent.be>
- * Sander Vrijders <sander.vrijders@ugent.be>
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -60,19 +60,42 @@
#include <errno.h>
#include <float.h>
-#define OPING_BUF_SIZE 1500
-
-#define ECHO_REQUEST 0
-#define ECHO_REPLY 1
-
+#define OPING_BUF_SIZE 1500
+#define ECHO_REQUEST 0
+#define ECHO_REPLY 1
#define OPING_MAX_FLOWS 256
-struct c {
+#define USAGE_STRING \
+"Usage: oping [OPTION]...\n" \
+"\n" \
+"Checks liveness between a client and a server\n" \
+"and reports the Round Trip Time (RTT)\n" \
+"\n" \
+" -l, --listen Run in server mode\n" \
+" --poll Server uses polling (lower latency)\n" \
+" --busy Server uses busy-poll (single flow)\n" \
+"\n" \
+" -c, --count Number of packets\n" \
+" -d, --duration Duration of the test (default 1s)\n" \
+" -f, --flood Send back-to-back without waiting\n" \
+" -F, --flood-busy Flood with busy-polling (lower latency)\n" \
+" -i, --interval Interval (default 1000ms)\n" \
+" -n, --server-name Name of the oping server\n" \
+" -q, --qos QoS (raw, best, video, voice, data)\n" \
+" -s, --size Payload size (B, default 64)\n" \
+" -Q, --quiet Only print final statistics\n" \
+" -D, --timeofday Print time of day before each line\n" \
+"\n" \
+" --help Display this help text and exit\n" \
+
+struct {
char * s_apn;
int interval;
uint32_t count;
int size;
bool timestamp;
+ bool flood;
+ bool flood_busy;
qosspec_t qs;
/* stats */
@@ -90,12 +113,16 @@ struct c {
pthread_t writer_pt;
} client;
-struct s {
+struct {
struct timespec times[OPING_MAX_FLOWS];
fset_t * flows;
fqueue_t * fq;
pthread_mutex_t lock;
+ bool quiet;
+ bool poll;
+ bool busy;
+
pthread_t cleaner_pt;
pthread_t accept_pt;
pthread_t server_pt;
@@ -114,22 +141,7 @@ struct oping_msg {
static void usage(void)
{
- printf("Usage: oping [OPTION]...\n"
- "Checks liveness between a client and a server\n"
- "and reports the Round Trip Time (RTT)\n\n"
- " -l, --listen Run in server mode\n"
- "\n"
- " -c, --count Number of packets\n"
- " -d, --duration Duration of the test (default 1s)\n"
- " -i, --interval Interval (default 1000ms)\n"
- " -n, --server-name Name of the oping server\n"
- " -q, --qos QoS (raw, best, video, voice, data)"
- "\n"
- " -s, --size Payload size (B, default 64)\n"
- " -Q, --quiet Only print final statistics\n"
- " -D, --timeofday Print time of day before each line"
- "\n"
- " --help Display this help text and exit\n");
+ printf(USAGE_STRING);
}
/* Times are in ms. */
@@ -168,46 +180,67 @@ int main(int argc,
client.size = 64;
client.count = INT_MAX;
client.timestamp = false;
+ client.flood = false;
+ client.flood_busy = false;
client.qs = qos_raw;
client.quiet = false;
+ server.quiet = false;
+ server.poll = false;
+ server.busy = false;
while (argc > 0) {
- if (strcmp(*argv, "-i") == 0 ||
- strcmp(*argv, "--interval") == 0) {
+ if ((strcmp(*argv, "-i") == 0 ||
+ strcmp(*argv, "--interval") == 0) &&
+ argc > 1) {
client.interval = strtol(*(++argv), &rem, 10);
client.interval *= time_mul(rem);
--argc;
- } else if (strcmp(*argv, "-n") == 0 ||
- strcmp(*argv, "--server-name") == 0) {
+ } else if ((strcmp(*argv, "-n") == 0 ||
+ strcmp(*argv, "--server-name") == 0) &&
+ argc > 1) {
client.s_apn = *(++argv);
--argc;
- } else if (strcmp(*argv, "-c") == 0 ||
- strcmp(*argv, "--count") == 0) {
+ } else if ((strcmp(*argv, "-c") == 0 ||
+ strcmp(*argv, "--count") == 0) &&
+ argc > 1) {
client.count = strtol(*(++argv), &rem, 10);
--argc;
- } else if (strcmp(*argv, "-d") == 0 ||
- strcmp(*argv, "--duration") == 0) {
+ } else if ((strcmp(*argv, "-d") == 0 ||
+ strcmp(*argv, "--duration") == 0) &&
+ argc > 1) {
duration = strtol(*(++argv), &rem, 10);
duration *= time_mul(rem);
--argc;
- } else if (strcmp(*argv, "-s") == 0 ||
- strcmp(*argv, "--size") == 0) {
+ } else if ((strcmp(*argv, "-s") == 0 ||
+ strcmp(*argv, "--size") == 0) &&
+ argc > 1) {
client.size = strtol(*(++argv), &rem, 10);
--argc;
- } else if (strcmp(*argv, "-q") == 0 ||
- strcmp(*argv, "--qos") == 0) {
+ } else if ((strcmp(*argv, "-q") == 0 ||
+ strcmp(*argv, "--qos") == 0) &&
+ argc > 1) {
qos = *(++argv);
--argc;
} else if (strcmp(*argv, "-l") == 0 ||
strcmp(*argv, "--listen") == 0) {
serv = true;
+ } else if (strcmp(*argv, "-f") == 0 ||
+ strcmp(*argv, "--flood") == 0) {
+ client.flood = true;
+ } else if (strcmp(*argv, "-F") == 0 ||
+ strcmp(*argv, "--flood-busy") == 0) {
+ client.flood_busy = true;
} else if (strcmp(*argv, "-D") == 0 ||
strcmp(*argv, "--timeofday") == 0) {
client.timestamp = true;
} else if (strcmp(*argv, "-Q") == 0 ||
strcmp(*argv, "--quiet") == 0) {
client.quiet = true;
-
+ server.quiet = true;
+ } else if (strcmp(*argv, "--poll") == 0) {
+ server.poll = true;
+ } else if (strcmp(*argv, "--busy") == 0) {
+ server.busy = true;
} else {
goto fail;
}
@@ -233,8 +266,6 @@ int main(int argc,
client.qs = qos_voice;
else if (strcmp(qos, "data") == 0)
client.qs = qos_data;
- else if (strcmp(qos, "raw_crypt") == 0)
- client.qs = qos_raw_crypt;
else
printf("Unknown QoS cube, defaulting to raw.\n");
}
diff --git a/src/tools/oping/oping_client.c b/src/tools/oping/oping_client.c
index 55ef6dc5..23807f65 100644
--- a/src/tools/oping/oping_client.c
+++ b/src/tools/oping/oping_client.c
@@ -1,10 +1,10 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2020
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* Ouroboros ping application
*
- * Dimitri Staessens <dimitri.staessens@ugent.be>
- * Sander Vrijders <sander.vrijders@ugent.be>
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -53,6 +53,40 @@ void shutdown_client(int signo, siginfo_t * info, void * c)
}
}
+static void update_rtt_stats(double ms)
+{
+ double d;
+
+ if (ms < client.rtt_min)
+ client.rtt_min = ms;
+ if (ms > client.rtt_max)
+ client.rtt_max = ms;
+
+ d = (ms - client.rtt_avg);
+ client.rtt_avg += d / client.rcvd;
+ client.rtt_m2 += d * (ms - client.rtt_avg);
+}
+
+static double rtt_val(double ms)
+{
+ return ms < 0.1 ? ms * 1000 : ms;
+}
+
+static const char * rtt_unit(double ms)
+{
+ return ms < 0.1 ? "µs" : "ms";
+}
+
+static void print_rtt(int len, int seq,
+ double ms, const char * suf)
+{
+ printf("%d bytes from %s: seq=%d "
+ "time=%.3f %s%s\n",
+ len, client.s_apn, seq,
+ rtt_val(ms), rtt_unit(ms),
+ suf != NULL ? suf : "");
+}
+
void * reader(void * o)
{
struct timespec timeout = {client.interval / 1000 + 2, 0};
@@ -64,7 +98,6 @@ void * reader(void * o)
int fd = *((int *) o);
int msg_len = 0;
double ms = 0;
- double d = 0;
uint32_t exp_id = 0;
fccntl(fd, FLOWSRCVTIMEO, &timeout);
@@ -100,7 +133,7 @@ void * reader(void * o)
sent.tv_sec = msg->tv_sec;
sent.tv_nsec = msg->tv_nsec;
- ms = ts_diff_us(&sent, &now) / 1000.0;
+ ms = ts_diff_us(&now, &sent) / 1000.0;
if (id < exp_id)
++client.ooo;
@@ -114,22 +147,12 @@ void * reader(void * o)
(size_t) rtc.tv_nsec / 1000);
}
- printf("%d bytes from %s: seq=%d time=%.3f ms%s\n",
- msg_len,
- client.s_apn,
- ntohl(msg->id),
- ms,
- id < exp_id ? " [out-of-order]" : "");
+ print_rtt(msg_len, ntohl(msg->id), ms,
+ id < exp_id ?
+ " [out-of-order]" : NULL);
}
- if (ms < client.rtt_min)
- client.rtt_min = ms;
- if (ms > client.rtt_max)
- client.rtt_max = ms;
-
- d = (ms - client.rtt_avg);
- client.rtt_avg += d / client.rcvd;
- client.rtt_m2 += d * (ms - client.rtt_avg);
+ update_rtt_stats(ms);
if (id >= exp_id)
exp_id = id + 1;
@@ -163,7 +186,7 @@ void * writer(void * o)
printf("Pinging %s with %d bytes of data (%u packets):\n\n",
client.s_apn, client.size, client.count);
- pthread_cleanup_push((void (*) (void *)) free, buf);
+ pthread_cleanup_push(free, buf);
while (!stop && client.sent < client.count) {
nanosleep(&wait, NULL);
@@ -204,13 +227,174 @@ static void client_fini(void)
return;
}
+static void print_stats(struct timespec * tic,
+ struct timespec * toc)
+{
+ printf("\n");
+ printf("--- %s ping statistics ---\n", client.s_apn);
+ printf("%d packets transmitted, ", client.sent);
+ printf("%d received, ", client.rcvd);
+ printf("%zd out-of-order, ", client.ooo);
+ printf("%.0lf%% packet loss, ", client.sent == 0 ? 0 :
+ ceil(100 - (100 * (client.rcvd / (float) client.sent))));
+ printf("time: %.3f ms\n", ts_diff_us(toc, tic) / 1000.0);
+
+ if (client.rcvd > 0) {
+ double a = client.rtt_avg;
+ double f = a < 0.1 ? 1000 : 1;
+ printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/",
+ client.rtt_min * f, client.rtt_avg * f,
+ client.rtt_max * f);
+ if (client.rcvd > 1)
+ printf("%.3f %s\n",
+ sqrt(client.rtt_m2 /
+ (client.rcvd - 1)) * f,
+ rtt_unit(a));
+ else
+ printf("NaN %s\n", rtt_unit(a));
+ }
+}
+
+static int flood_busy_ping(int fd)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ struct timespec sent;
+ struct timespec rcvd;
+ double ms;
+ int n;
+
+ memset(buf, 0, client.size);
+
+ fccntl(fd, FLOWSFLAGS,
+ FLOWFRDWR | FLOWFRNOPART | FLOWFRNOBLOCK);
+
+ if (!client.quiet)
+ printf("Pinging %s with %d bytes"
+ " of data (%u packets,"
+ " busy-poll):\n\n",
+ client.s_apn, client.size,
+ client.count);
+
+ while (!stop && client.sent < client.count) {
+ clock_gettime(CLOCK_MONOTONIC, &sent);
+
+ msg->type = htonl(ECHO_REQUEST);
+ msg->id = htonl(client.sent);
+ msg->tv_sec = sent.tv_sec;
+ msg->tv_nsec = sent.tv_nsec;
+
+ if (flow_write(fd, buf,
+ client.size) < 0) {
+ printf("Failed to send "
+ "packet.\n");
+ break;
+ }
+
+ ++client.sent;
+
+ do {
+ n = flow_read(fd, buf,
+ OPING_BUF_SIZE);
+ } while (n == -EAGAIN && !stop);
+
+ if (n < 0)
+ break;
+
+ clock_gettime(CLOCK_MONOTONIC, &rcvd);
+
+ if (ntohl(msg->type) != ECHO_REPLY)
+ continue;
+
+ ++client.rcvd;
+
+ sent.tv_sec = msg->tv_sec;
+ sent.tv_nsec = msg->tv_nsec;
+ ms = ts_diff_us(&rcvd, &sent) / 1000.0;
+
+ update_rtt_stats(ms);
+
+ if (!client.quiet)
+ print_rtt(client.size,
+ ntohl(msg->id), ms,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int flood_ping(int fd)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ struct timespec sent;
+ struct timespec rcvd;
+ double ms;
+
+ memset(buf, 0, client.size);
+
+ if (!client.quiet)
+ printf("Pinging %s with %d bytes of data (%u packets):\n\n",
+ client.s_apn, client.size, client.count);
+
+ while (!stop && client.sent < client.count) {
+ clock_gettime(CLOCK_MONOTONIC, &sent);
+
+ msg->type = htonl(ECHO_REQUEST);
+ msg->id = htonl(client.sent);
+ msg->tv_sec = sent.tv_sec;
+ msg->tv_nsec = sent.tv_nsec;
+
+ if (flow_write(fd, buf, client.size) < 0) {
+ printf("Failed to send packet.\n");
+ break;
+ }
+
+ ++client.sent;
+
+ if (flow_read(fd, buf, OPING_BUF_SIZE) < 0) {
+ printf("Failed to read packet.\n");
+ break;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &rcvd);
+
+ if (ntohl(msg->type) != ECHO_REPLY)
+ continue;
+
+ ++client.rcvd;
+
+ sent.tv_sec = msg->tv_sec;
+ sent.tv_nsec = msg->tv_nsec;
+ ms = ts_diff_us(&rcvd, &sent) / 1000.0;
+
+ update_rtt_stats(ms);
+
+ if (!client.quiet)
+ print_rtt(client.size,
+ ntohl(msg->id), ms,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int threaded_ping(int fd)
+{
+ pthread_create(&client.reader_pt, NULL, reader, &fd);
+ pthread_create(&client.writer_pt, NULL, writer, &fd);
+
+ pthread_join(client.writer_pt, NULL);
+ pthread_join(client.reader_pt, NULL);
+
+ return 0;
+}
+
static int client_main(void)
{
struct sigaction sig_act;
-
struct timespec tic;
struct timespec toc;
-
int fd;
memset(&sig_act, 0, sizeof sig_act);
@@ -232,7 +416,7 @@ static int client_main(void)
fd = flow_alloc(client.s_apn, &client.qs, NULL);
if (fd < 0) {
- printf("Failed to allocate flow.\n");
+ printf("Failed to allocate flow: %d.\n", fd);
client_fini();
return -1;
}
@@ -241,37 +425,18 @@ static int client_main(void)
clock_gettime(CLOCK_REALTIME, &tic);
- pthread_create(&client.reader_pt, NULL, reader, &fd);
- pthread_create(&client.writer_pt, NULL, writer, &fd);
-
- pthread_join(client.writer_pt, NULL);
- pthread_join(client.reader_pt, NULL);
+ if (client.flood_busy)
+ flood_busy_ping(fd);
+ else if (client.flood)
+ flood_ping(fd);
+ else
+ threaded_ping(fd);
clock_gettime(CLOCK_REALTIME, &toc);
- printf("\n");
- printf("--- %s ping statistics ---\n", client.s_apn);
- printf("%d packets transmitted, ", client.sent);
- printf("%d received, ", client.rcvd);
- printf("%zd out-of-order, ", client.ooo);
- printf("%.0lf%% packet loss, ", client.sent == 0 ? 0 :
- ceil(100 - (100 * (client.rcvd / (float) client.sent))));
- printf("time: %.3f ms\n", ts_diff_us(&tic, &toc) / 1000.0);
-
- if (client.rcvd > 0) {
- printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/",
- client.rtt_min,
- client.rtt_avg,
- client.rtt_max);
- if (client.rcvd > 1)
- printf("%.3f ms\n",
- sqrt(client.rtt_m2 / (client.rcvd - 1)));
- else
- printf("NaN ms\n");
- }
+ print_stats(&tic, &toc);
flow_dealloc(fd);
-
client_fini();
return 0;
diff --git a/src/tools/oping/oping_server.c b/src/tools/oping/oping_server.c
index e7a7b12d..33af28c4 100644
--- a/src/tools/oping/oping_server.c
+++ b/src/tools/oping/oping_server.c
@@ -1,10 +1,10 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2020
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* Ouroboros ping application
*
- * Dimitri Staessens <dimitri.staessens@ugent.be>
- * Sander Vrijders <sander.vrijders@ugent.be>
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,6 +36,8 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+ #include <ouroboros/hash.h>
+
void shutdown_server(int signo, siginfo_t * info, void * c)
{
(void) info;
@@ -67,7 +69,7 @@ void * cleaner_thread(void * o)
time_t diff;
pthread_mutex_lock(&server.lock);
- diff = ts_diff_ms(&server.times[i], &now);
+ diff = ts_diff_ms(&now, &server.times[i]);
pthread_mutex_unlock(&server.lock);
if (diff > deadline_ms) {
@@ -87,12 +89,15 @@ void * server_thread(void *o)
struct oping_msg * msg = (struct oping_msg *) buf;
struct timespec now = {0, 0};
struct timespec timeout = {0, 100 * MILLION};
+ struct timespec poll_timeout = {0, 0};
int fd;
(void) o;
while (true) {
- if (fevent(server.flows, server.fq, &timeout) == -ETIMEDOUT)
+ if (fevent(server.flows, server.fq,
+ server.poll ? &poll_timeout : &timeout)
+ == -ETIMEDOUT)
continue;
while ((fd = fqueue_next(server.fq)) >= 0) {
@@ -100,13 +105,15 @@ void * server_thread(void *o)
if (msg_len < 0)
continue;
+ if (!server.quiet)
+ printf("Received %d bytes on fd %d.\n",
+ msg_len, fd);
+
if (ntohl(msg->type) != ECHO_REQUEST) {
printf("Invalid message on fd %d.\n", fd);
continue;
}
- printf("Received %d bytes on fd %d.\n", msg_len, fd);
-
clock_gettime(CLOCK_REALTIME, &now);
pthread_mutex_lock(&server.lock);
@@ -131,13 +138,16 @@ void * accept_thread(void * o)
(void) o;
- printf("Ouroboros ping server started.\n");
+ printf("Ouroboros ping server started.");
+ if (server.busy)
+ printf(" [busy-poll]");
+ printf("\n");
while (true) {
fd = flow_accept(&qs, NULL);
if (fd < 0) {
- printf("Failed to accept flow.\n");
- break;
+ printf("Failed to accept flow: %d \n", fd);
+ continue;
}
printf("New flow %d.\n", fd);
@@ -151,12 +161,56 @@ void * accept_thread(void * o)
pthread_mutex_unlock(&server.lock);
fccntl(fd, FLOWSFLAGS,
- FLOWFRNOBLOCK | FLOWFRDWR | FLOWFRNOPART);
+ FLOWFRNOBLOCK | FLOWFRDWR
+ | FLOWFRNOPART);
}
return (void *) 0;
}
+void * busy_thread(void * o)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ int fd;
+ int msg_len;
+
+ (void) o;
+
+ /* Accept a single flow. */
+ fd = flow_accept(NULL, NULL);
+ if (fd < 0) {
+ printf("Failed to accept flow.\n");
+ return (void *) -1;
+ }
+
+ printf("New flow %d (busy-poll).\n", fd);
+
+ fccntl(fd, FLOWSFLAGS,
+ FLOWFRNOBLOCK | FLOWFRDWR
+ | FLOWFRNOPART);
+
+ while (true) {
+ msg_len = flow_read(fd, buf,
+ OPING_BUF_SIZE);
+ if (msg_len == -EAGAIN)
+ continue;
+ if (msg_len < 0)
+ break;
+
+ if (ntohl(msg->type) != ECHO_REQUEST)
+ continue;
+
+ msg->type = htonl(ECHO_REPLY);
+
+ flow_write(fd, buf, msg_len);
+ }
+
+ flow_dealloc(fd);
+
+ return (void *) 0;
+}
+
int server_main(void)
{
struct sigaction sig_act;
@@ -184,12 +238,21 @@ int server_main(void)
}
pthread_create(&server.cleaner_pt, NULL, cleaner_thread, NULL);
- pthread_create(&server.accept_pt, NULL, accept_thread, NULL);
- pthread_create(&server.server_pt, NULL, server_thread, NULL);
- pthread_join(server.accept_pt, NULL);
+ if (server.busy) {
+ pthread_create(&server.server_pt, NULL,
+ busy_thread, NULL);
+ pthread_join(server.server_pt, NULL);
+ pthread_cancel(server.cleaner_pt);
+ } else {
+ pthread_create(&server.accept_pt, NULL,
+ accept_thread, NULL);
+ pthread_create(&server.server_pt, NULL,
+ server_thread, NULL);
+ pthread_join(server.accept_pt, NULL);
+ pthread_cancel(server.server_pt);
+ }
- pthread_cancel(server.server_pt);
pthread_cancel(server.cleaner_pt);
fset_destroy(server.flows);