Bidirect mode implementation (#780)
Adds the `--bidir` flag to support simultaneous two-way tests.
Submitted by @LikHait. Fixes #201.
diff --git a/docs/invoking.rst b/docs/invoking.rst
index 3f20f32..79d6b95 100644
--- a/docs/invoking.rst
+++ b/docs/invoking.rst
@@ -292,6 +292,9 @@
-R, --reverse
reverse the direction of a test, so that the server sends data
to the client
+
+ --bidir
+ bidirectional mode, server and client send and receive data.
-w, --window n[KM]
window size / socket buffer size (this gets sent to the server
diff --git a/src/iperf.h b/src/iperf.h
index 26e4f4f..b8a6e58 100755
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -167,6 +167,7 @@
int remote_port;
int socket;
int id;
+ int sender;
/* XXX: is settings just a pointer to the same struct in iperf_test? if not,
should it be? */
struct iperf_settings *settings; /* pointer to structure settings */
@@ -234,11 +235,18 @@
TAILQ_ENTRY(xbind_entry) link;
};
+enum iperf_mode {
+ SENDER = 1,
+ RECEIVER = 0,
+ BIDIRECTIONAL = -1
+};
+
struct iperf_test
{
char role; /* 'c' lient or 's' erver */
- int sender; /* client & !reverse or server & reverse */
+ enum iperf_mode mode;
int sender_has_retransmits;
+ int other_side_has_retransmits; /* used if mode == BIDIRECTIONAL */
struct protocol *protocol;
signed char state;
char *server_hostname; /* -c option */
@@ -280,6 +288,7 @@
int one_off; /* -1 option */
int no_delay; /* -N option */
int reverse; /* -R option */
+ int bidirectional; /* --bidirectional */
int verbose; /* -V option - verbose mode */
int json_output; /* -J option - JSON output */
int zerocopy; /* -Z option - use sendfile */
@@ -315,6 +324,10 @@
iperf_size_t bytes_sent;
iperf_size_t blocks_sent;
+
+ iperf_size_t bytes_received;
+ iperf_size_t blocks_received;
+
char cookie[COOKIE_SIZE];
// struct iperf_stream *streams; /* pointer to list of struct stream */
SLIST_HEAD(slisthead, iperf_stream) streams;
diff --git a/src/iperf_api.c b/src/iperf_api.c
index d6c4717..db6fae0 100755
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -435,7 +435,7 @@
static void
check_sender_has_retransmits(struct iperf_test *ipt)
{
- if (ipt->sender && ipt->protocol->id == Ptcp && has_tcpinfo_retransmits())
+ if (ipt->mode != RECEIVER && ipt->protocol->id == Ptcp && has_tcpinfo_retransmits())
ipt->sender_has_retransmits = 1;
else
ipt->sender_has_retransmits = 0;
@@ -445,12 +445,17 @@
iperf_set_test_role(struct iperf_test *ipt, char role)
{
ipt->role = role;
- if (role == 'c')
- ipt->sender = 1;
- else if (role == 's')
- ipt->sender = 0;
- if (ipt->reverse)
- ipt->sender = ! ipt->sender;
+ if (!ipt->reverse) {
+ if (role == 'c')
+ ipt->mode = SENDER;
+ else if (role == 's')
+ ipt->mode = RECEIVER;
+ } else {
+ if (role == 'c')
+ ipt->mode = RECEIVER;
+ else if (role == 's')
+ ipt->mode = SENDER;
+ }
check_sender_has_retransmits(ipt);
}
@@ -470,12 +475,17 @@
iperf_set_test_reverse(struct iperf_test *ipt, int reverse)
{
ipt->reverse = reverse;
- if (ipt->role == 'c')
- ipt->sender = 1;
- else
- ipt->sender = 0;
- if (ipt->reverse)
- ipt->sender = ! ipt->sender;
+ if (!ipt->reverse) {
+ if (ipt->role == 'c')
+ ipt->mode = SENDER;
+ else if (ipt->role == 's')
+ ipt->mode = RECEIVER;
+ } else {
+ if (ipt->role == 'c')
+ ipt->mode = RECEIVER;
+ else if (ipt->role == 's')
+ ipt->mode = SENDER;
+ }
check_sender_has_retransmits(ipt);
}
@@ -559,6 +569,16 @@
ipt->extra_data = dat;
}
+void
+iperf_set_test_bidirectional(struct iperf_test* ipt, int bidirectional)
+{
+ ipt->bidirectional = bidirectional;
+ if (bidirectional)
+ ipt->mode = BIDIRECTIONAL;
+ else
+ iperf_set_test_reverse(ipt, ipt->reverse);
+}
+
/********************** Get/set test protocol structure ***********************/
struct protocol *
@@ -741,6 +761,7 @@
{"length", required_argument, NULL, 'l'},
{"parallel", required_argument, NULL, 'P'},
{"reverse", no_argument, NULL, 'R'},
+ {"bidir", no_argument, NULL, OPT_BIDIRECTIONAL},
{"window", required_argument, NULL, 'w'},
{"bind", required_argument, NULL, 'B'},
{"cport", required_argument, NULL, OPT_CLIENT_PORT},
@@ -943,9 +964,21 @@
client_flag = 1;
break;
case 'R':
+ if (test->bidirectional) {
+ i_errno = IEREVERSEBIDIR;
+ return -1;
+ }
iperf_set_test_reverse(test, 1);
client_flag = 1;
break;
+ case OPT_BIDIRECTIONAL:
+ if (test->reverse) {
+ i_errno = IEREVERSEBIDIR;
+ return -1;
+ }
+ iperf_set_test_bidirectional(test, 1);
+ client_flag = 1;
+ break;
case 'w':
// XXX: This is a socket buffer, not specific to TCP
// Do sanity checks as double-precision floating point
@@ -1344,7 +1377,7 @@
iperf_time_now(&now);
streams_active = 0;
SLIST_FOREACH(sp, &test->streams, streams) {
- if ((sp->green_light &&
+ if ((sp->green_light && sp->sender &&
(write_setP == NULL || FD_ISSET(sp->socket, write_setP)))) {
if ((r = sp->snd(sp)) < 0) {
if (r == NET_SOFTERROR)
@@ -1386,13 +1419,13 @@
struct iperf_stream *sp;
SLIST_FOREACH(sp, &test->streams, streams) {
- if (FD_ISSET(sp->socket, read_setP)) {
+ if (FD_ISSET(sp->socket, read_setP) && !sp->sender) {
if ((r = sp->rcv(sp)) < 0) {
i_errno = IESTREAMREAD;
return r;
}
- test->bytes_sent += r;
- ++test->blocks_sent;
+ test->bytes_received += r;
+ ++test->blocks_received;
FD_CLR(sp->socket, read_setP);
}
}
@@ -1606,6 +1639,8 @@
cJSON_AddNumberToObject(j, "parallel", test->num_streams);
if (test->reverse)
cJSON_AddTrueToObject(j, "reverse");
+ if (test->bidirectional)
+ cJSON_AddTrueToObject(j, "bidirectional");
if (test->settings->socket_bufsize)
cJSON_AddNumberToObject(j, "window", test->settings->socket_bufsize);
if (test->settings->blksize)
@@ -1697,6 +1732,8 @@
test->num_streams = j_p->valueint;
if ((j_p = cJSON_GetObjectItem(j, "reverse")) != NULL)
iperf_set_test_reverse(test, 1);
+ if ((j_p = cJSON_GetObjectItem(j, "bidirectional")) != NULL)
+ iperf_set_test_bidirectional(test, 1);
if ((j_p = cJSON_GetObjectItem(j, "window")) != NULL)
test->settings->socket_bufsize = j_p->valueint;
if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL)
@@ -1729,7 +1766,7 @@
if ((j_p = cJSON_GetObjectItem(j, "authtoken")) != NULL)
test->settings->authtoken = strdup(j_p->valuestring);
#endif //HAVE_SSL
- if (test->sender && test->protocol->id == Ptcp && has_tcpinfo_retransmits())
+ if (test->mode && test->protocol->id == Ptcp && has_tcpinfo_retransmits())
test->sender_has_retransmits = 1;
if (test->settings->rate)
cJSON_AddNumberToObject(test->json_start, "target_bitrate", test->settings->rate);
@@ -1762,7 +1799,7 @@
cJSON_AddNumberToObject(j, "cpu_util_total", test->cpu_util[0]);
cJSON_AddNumberToObject(j, "cpu_util_user", test->cpu_util[1]);
cJSON_AddNumberToObject(j, "cpu_util_system", test->cpu_util[2]);
- if ( ! test->sender )
+ if ( test->mode == RECEIVER )
sender_has_retransmits = -1;
else
sender_has_retransmits = test->sender_has_retransmits;
@@ -1812,8 +1849,8 @@
r = -1;
} else {
cJSON_AddItemToArray(j_streams, j_stream);
- bytes_transferred = test->sender ? (sp->result->bytes_sent - sp->result->bytes_sent_omit) : sp->result->bytes_received;
- retransmits = (test->sender && test->sender_has_retransmits) ? sp->result->stream_retrans : -1;
+ bytes_transferred = sp->sender ? (sp->result->bytes_sent - sp->result->bytes_sent_omit) : sp->result->bytes_received;
+ retransmits = (sp->sender && test->sender_has_retransmits) ? sp->result->stream_retrans : -1;
cJSON_AddNumberToObject(j_stream, "id", sp->id);
cJSON_AddNumberToObject(j_stream, "bytes", bytes_transferred);
cJSON_AddNumberToObject(j_stream, "retransmits", retransmits);
@@ -1894,8 +1931,13 @@
test->remote_cpu_util[1] = j_cpu_util_user->valuedouble;
test->remote_cpu_util[2] = j_cpu_util_system->valuedouble;
result_has_retransmits = j_sender_has_retransmits->valueint;
- if (! test->sender)
- test->sender_has_retransmits = result_has_retransmits;
+ if ( test->mode == RECEIVER ) {
+ test->sender_has_retransmits = result_has_retransmits;
+ test->other_side_has_retransmits = 0;
+ }
+ else if ( test->mode == BIDIRECTIONAL )
+ test->other_side_has_retransmits = result_has_retransmits;
+
j_streams = cJSON_GetObjectItem(j, "streams");
if (j_streams == NULL) {
i_errno = IERECVRESULTS;
@@ -1932,7 +1974,7 @@
i_errno = IESTREAMID;
r = -1;
} else {
- if (test->sender) {
+ if (sp->sender) {
sp->jitter = jitter;
sp->cnt_error = cerror;
sp->peer_packet_count = pcount;
@@ -2195,6 +2237,7 @@
testp->server_port = PORT;
testp->ctrl_sck = -1;
testp->prot_listener = -1;
+ testp->other_side_has_retransmits = 0;
testp->stats_callback = iperf_stats_callback;
testp->reporter_callback = iperf_reporter_callback;
@@ -2416,7 +2459,7 @@
SLIST_INIT(&test->streams);
test->role = 's';
- test->sender = 0;
+ test->mode = RECEIVER;
test->sender_has_retransmits = 0;
set_protocol(test, Ptcp);
test->omit = OMIT;
@@ -2433,7 +2476,13 @@
test->bytes_sent = 0;
test->blocks_sent = 0;
+ test->bytes_received = 0;
+ test->blocks_received = 0;
+
+ test->other_side_has_retransmits = 0;
+
test->reverse = 0;
+ test->bidirectional = 0;
test->no_delay = 0;
FD_ZERO(&test->read_set);
@@ -2539,8 +2588,7 @@
temp.omitted = test->omitting;
SLIST_FOREACH(sp, &test->streams, streams) {
rp = sp->result;
-
- temp.bytes_transferred = test->sender ? rp->bytes_sent_this_interval : rp->bytes_received_this_interval;
+ temp.bytes_transferred = sp->sender ? rp->bytes_sent_this_interval : rp->bytes_received_this_interval;
irp = TAILQ_LAST(&rp->interval_results, irlisthead);
/* result->end_time contains timestamp of previous interval */
@@ -2611,19 +2659,14 @@
static void
iperf_print_intermediate(struct iperf_test *test)
{
- char ubuf[UNIT_LEN];
- char nbuf[UNIT_LEN];
struct iperf_stream *sp = NULL;
struct iperf_interval_results *irp;
- iperf_size_t bytes = 0;
- double bandwidth;
- int retransmits = 0;
- double start_time, end_time;
struct iperf_time temp_time;
cJSON *json_interval;
cJSON *json_interval_streams;
- int total_packets = 0, lost_packets = 0;
- double avg_jitter = 0.0, lost_percent;
+
+ int lower_mode, upper_mode;
+ int current_mode;
/*
* Due to timing oddities, there can be cases, especially on the
@@ -2683,31 +2726,81 @@
json_interval_streams = NULL;
}
- SLIST_FOREACH(sp, &test->streams, streams) {
- print_interval_results(test, sp, json_interval_streams);
- /* sum up all streams */
- irp = TAILQ_LAST(&sp->result->interval_results, irlisthead);
- if (irp == NULL) {
- iperf_err(test, "iperf_print_intermediate error: interval_results is NULL");
- return;
- }
- bytes += irp->bytes_transferred;
- if (test->protocol->id == Ptcp) {
- if (test->sender_has_retransmits == 1) {
- retransmits += irp->interval_retrans;
- }
- } else {
- total_packets += irp->interval_packet_count;
- lost_packets += irp->interval_cnt_error;
- avg_jitter += irp->jitter;
- }
+ /*
+ * We must to sum streams separately.
+ * For bidirectional mode we must to display
+ * information about sender and receiver streams.
+ * For client side we must handle sender streams
+ * firstly and receiver streams for server side.
+ * The following design allows us to do this.
+ */
+
+ if (test->mode == BIDIRECTIONAL) {
+ if (test->role == 'c') {
+ lower_mode = -1;
+ upper_mode = 0;
+ } else {
+ lower_mode = 0;
+ upper_mode = 1;
+ }
+ } else {
+ lower_mode = test->mode;
+ upper_mode = lower_mode;
}
- /* next build string with sum of all streams */
- if (test->num_streams > 1 || test->json_output) {
- sp = SLIST_FIRST(&test->streams); /* reset back to 1st stream */
- /* Only do this of course if there was a first stream */
- if (sp) {
+
+ for (current_mode = lower_mode; current_mode <= upper_mode; ++current_mode) {
+ char ubuf[UNIT_LEN];
+ char nbuf[UNIT_LEN];
+ char mbuf[UNIT_LEN];
+ char zbuf[] = " ";
+
+ iperf_size_t bytes = 0;
+ double bandwidth;
+ int retransmits = 0;
+ double start_time, end_time;
+
+ int total_packets = 0, lost_packets = 0;
+ double avg_jitter = 0.0, lost_percent;
+ int stream_must_be_sender = current_mode * current_mode;
+
+ /* Print stream role just for bidirectional mode. */
+
+ if (test->mode == BIDIRECTIONAL) {
+ sprintf(mbuf, "[%s-%s]", stream_must_be_sender?"TX":"RX", test->role == 'c'?"C":"S");
+ } else {
+ mbuf[0] = '\0';
+ zbuf[0] = '\0';
+ }
+
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender == stream_must_be_sender) {
+ print_interval_results(test, sp, json_interval_streams);
+ /* sum up all streams */
+ irp = TAILQ_LAST(&sp->result->interval_results, irlisthead);
+ if (irp == NULL) {
+ iperf_err(test,
+ "iperf_print_intermediate error: interval_results is NULL");
+ return;
+ }
+ bytes += irp->bytes_transferred;
+ if (test->protocol->id == Ptcp) {
+ if (test->sender_has_retransmits == 1) {
+ retransmits += irp->interval_retrans;
+ }
+ } else {
+ total_packets += irp->interval_packet_count;
+ lost_packets += irp->interval_cnt_error;
+ avg_jitter += irp->jitter;
+ }
+ }
+ }
+
+ /* next build string with sum of all streams */
+ if (test->num_streams > 1 || test->json_output) {
+ sp = SLIST_FIRST(&test->streams); /* reset back to 1st stream */
+ /* Only do this of course if there was a first stream */
+ if (sp) {
irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); /* use 1st stream for timing info */
unit_snprintf(ubuf, UNIT_LEN, (double) bytes, 'A');
@@ -2718,42 +2811,43 @@
start_time = iperf_time_in_secs(&temp_time);
iperf_time_diff(&sp->result->start_time,&irp->interval_end_time, &temp_time);
end_time = iperf_time_in_secs(&temp_time);
- if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits == 1) {
- /* Interval sum, TCP with retransmits. */
- if (test->json_output)
- cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d omitted: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (int64_t) retransmits, irp->omitted)); /* XXX irp->omitted or test->omitting? */
- else
- iperf_printf(test, report_sum_bw_retrans_format, start_time, end_time, ubuf, nbuf, retransmits, irp->omitted?report_omitted:""); /* XXX irp->omitted or test->omitting? */
- } else {
- /* Interval sum, TCP without retransmits. */
- if (test->json_output)
- cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f omitted: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, test->omitting));
- else
- iperf_printf(test, report_sum_bw_format, start_time, end_time, ubuf, nbuf, test->omitting?report_omitted:"");
- }
- } else {
- /* Interval sum, UDP. */
- if (test->sender) {
- if (test->json_output)
- cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f packets: %d omitted: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (int64_t) total_packets, test->omitting));
- else
- iperf_printf(test, report_sum_bw_udp_sender_format, start_time, end_time, ubuf, nbuf, total_packets, test->omitting?report_omitted:"");
- } else {
- avg_jitter /= test->num_streams;
- if (total_packets > 0) {
- lost_percent = 100.0 * lost_packets / total_packets;
- }
- else {
- lost_percent = 0.0;
- }
- if (test->json_output)
- cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f omitted: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) total_packets, (double) lost_percent, test->omitting));
- else
- iperf_printf(test, report_sum_bw_udp_format, start_time, end_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, total_packets, lost_percent, test->omitting?report_omitted:"");
- }
- }
- }
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits == 1 && stream_must_be_sender) {
+ /* Interval sum, TCP with retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (int64_t) retransmits, irp->omitted, stream_must_be_sender)); /* XXX irp->omitted or test->omitting? */
+ else
+ iperf_printf(test, report_sum_bw_retrans_format, mbuf, start_time, end_time, ubuf, nbuf, retransmits, irp->omitted?report_omitted:""); /* XXX irp->omitted or test->omitting? */
+ } else {
+ /* Interval sum, TCP without retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, test->omitting, stream_must_be_sender));
+ else
+ iperf_printf(test, report_sum_bw_format, mbuf, start_time, end_time, ubuf, nbuf, test->omitting?report_omitted:"");
+ }
+ } else {
+ /* Interval sum, UDP. */
+ if (stream_must_be_sender) {
+ if (test->json_output)
+ cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f packets: %d omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (int64_t) total_packets, test->omitting, stream_must_be_sender));
+ else
+ iperf_printf(test, report_sum_bw_udp_sender_format, mbuf, start_time, end_time, ubuf, nbuf, zbuf, total_packets, test->omitting?report_omitted:"");
+ } else {
+ avg_jitter /= test->num_streams;
+ if (total_packets > 0) {
+ lost_percent = 100.0 * lost_packets / total_packets;
+ }
+ else {
+ lost_percent = 0.0;
+ }
+ if (test->json_output)
+ cJSON_AddItemToObject(json_interval, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) total_packets, (double) lost_percent, test->omitting, stream_must_be_sender));
+ else
+ iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, end_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, total_packets, lost_percent, test->omitting?report_omitted:"");
+ }
+ }
+ }
+ }
}
}
@@ -2765,22 +2859,11 @@
{
cJSON *json_summary_streams = NULL;
- cJSON *json_summary_stream = NULL;
- int total_retransmits = 0;
- int total_packets = 0, lost_packets = 0;
- int sender_packet_count = 0, receiver_packet_count = 0; /* for this stream, this interval */
- int sender_total_packets = 0, receiver_total_packets = 0; /* running total */
- char ubuf[UNIT_LEN];
- char nbuf[UNIT_LEN];
- struct stat sb;
- char sbuf[UNIT_LEN];
- struct iperf_stream *sp = NULL;
- iperf_size_t bytes_sent, total_sent = 0;
- iperf_size_t bytes_received, total_received = 0;
- double start_time, end_time = 0.0, avg_jitter = 0.0, lost_percent = 0.0;
- double sender_time = 0.0, receiver_time = 0.0;
- struct iperf_time temp_time;
- double bandwidth;
+
+ int lower_mode, upper_mode;
+ int current_mode;
+
+ int tmp_sender_has_retransmits = test->sender_has_retransmits;
/* print final summary for all intervals */
@@ -2794,385 +2877,470 @@
if (test->verbose)
iperf_printf(test, "%s", report_summary);
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits)
- iperf_printf(test, "%s", report_bw_retrans_header);
+ if (test->sender_has_retransmits || test->other_side_has_retransmits) {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_retrans_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_retrans_header);
+ }
+ else {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_header);
+ }
+ } else {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_udp_header_bidir);
else
- iperf_printf(test, "%s", report_bw_header);
- } else
- iperf_printf(test, "%s", report_bw_udp_header);
+ iperf_printf(test, "%s", report_bw_udp_header);
+ }
}
- start_time = 0.;
- sp = SLIST_FIRST(&test->streams);
- /*
- * If there is at least one stream, then figure out the length of time
- * we were running the tests and print out some statistics about
- * the streams. It's possible to not have any streams at all
- * if the client got interrupted before it got to do anything.
- *
- * Also note that we try to keep seperate values for the sender
- * and receiver ending times. Earlier iperf (3.1 and earlier)
- * servers didn't send that to the clients, so in this case we fall
- * back to using the client's ending timestamp. The fallback is
- * basically emulating what iperf 3.1 did.
+ /*
+ * We must to sum streams separately.
+ * For bidirectional mode we must to display
+ * information about sender and receiver streams.
+ * For client side we must handle sender streams
+ * firstly and receiver streams for server side.
+ * The following design allows us to do this.
*/
- if (sp) {
+
+ if (test->mode == BIDIRECTIONAL) {
+ if (test->role == 'c') {
+ lower_mode = -1;
+ upper_mode = 0;
+ } else {
+ lower_mode = 0;
+ upper_mode = 1;
+ }
+ } else {
+ lower_mode = test->mode;
+ upper_mode = lower_mode;
+ }
+
+
+ for (current_mode = lower_mode; current_mode <= upper_mode; ++current_mode) {
+ cJSON *json_summary_stream = NULL;
+ int total_retransmits = 0;
+ int total_packets = 0, lost_packets = 0;
+ int sender_packet_count = 0, receiver_packet_count = 0; /* for this stream, this interval */
+ int sender_total_packets = 0, receiver_total_packets = 0; /* running total */
+ char ubuf[UNIT_LEN];
+ char nbuf[UNIT_LEN];
+ struct stat sb;
+ char sbuf[UNIT_LEN];
+ struct iperf_stream *sp = NULL;
+ iperf_size_t bytes_sent, total_sent = 0;
+ iperf_size_t bytes_received, total_received = 0;
+ double start_time, end_time = 0.0, avg_jitter = 0.0, lost_percent = 0.0;
+ double sender_time = 0.0, receiver_time = 0.0;
+ struct iperf_time temp_time;
+ double bandwidth;
+
+ char mbuf[UNIT_LEN];
+ int stream_must_be_sender = current_mode * current_mode;
+
+
+ /* Print stream role just for bidirectional mode. */
+
+ if (test->mode == BIDIRECTIONAL) {
+ sprintf(mbuf, "[%s-%s]", stream_must_be_sender?"TX":"RX", test->role == 'c'?"C":"S");
+ } else {
+ mbuf[0] = '\0';
+ }
+
+ /* Get sender_has_retransmits for each sender side (client and server) */
+ if (test->mode == BIDIRECTIONAL && stream_must_be_sender)
+ test->sender_has_retransmits = tmp_sender_has_retransmits;
+ else if (test->mode == BIDIRECTIONAL && !stream_must_be_sender)
+ test->sender_has_retransmits = test->other_side_has_retransmits;
+
+ start_time = 0.;
+ sp = SLIST_FIRST(&test->streams);
+
+ /*
+ * If there is at least one stream, then figure out the length of time
+ * we were running the tests and print out some statistics about
+ * the streams. It's possible to not have any streams at all
+ * if the client got interrupted before it got to do anything.
+ *
+ * Also note that we try to keep seperate values for the sender
+ * and receiver ending times. Earlier iperf (3.1 and earlier)
+ * servers didn't send that to the clients, so in this case we fall
+ * back to using the client's ending timestamp. The fallback is
+ * basically emulating what iperf 3.1 did.
+ */
+
+ if (sp) {
iperf_time_diff(&sp->result->start_time, &sp->result->end_time, &temp_time);
end_time = iperf_time_in_secs(&temp_time);
- if (test->sender) {
- sp->result->sender_time = end_time;
- if (sp->result->receiver_time == 0.0) {
- sp->result->receiver_time = sp->result->sender_time;
- }
- }
- else {
- sp->result->receiver_time = end_time;
- if (sp->result->sender_time == 0.0) {
- sp->result->sender_time = sp->result->receiver_time;
- }
- }
- sender_time = sp->result->sender_time;
- receiver_time = sp->result->receiver_time;
- SLIST_FOREACH(sp, &test->streams, streams) {
- if (test->json_output) {
- json_summary_stream = cJSON_CreateObject();
- if (json_summary_stream == NULL)
- return;
- cJSON_AddItemToArray(json_summary_streams, json_summary_stream);
- }
+ if (sp->sender) {
+ sp->result->sender_time = end_time;
+ if (sp->result->receiver_time == 0.0) {
+ sp->result->receiver_time = sp->result->sender_time;
+ }
+ }
+ else {
+ sp->result->receiver_time = end_time;
+ if (sp->result->sender_time == 0.0) {
+ sp->result->sender_time = sp->result->receiver_time;
+ }
+ }
+ sender_time = sp->result->sender_time;
+ receiver_time = sp->result->receiver_time;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender == stream_must_be_sender) {
+ if (test->json_output) {
+ json_summary_stream = cJSON_CreateObject();
+ if (json_summary_stream == NULL)
+ return;
+ cJSON_AddItemToArray(json_summary_streams, json_summary_stream);
+ }
- bytes_sent = sp->result->bytes_sent - sp->result->bytes_sent_omit;
- bytes_received = sp->result->bytes_received;
- total_sent += bytes_sent;
- total_received += bytes_received;
+ bytes_sent = sp->result->bytes_sent - sp->result->bytes_sent_omit;
+ bytes_received = sp->result->bytes_received;
+ total_sent += bytes_sent;
+ total_received += bytes_received;
- if (test->sender) {
- sender_packet_count = sp->packet_count;
- receiver_packet_count = sp->peer_packet_count;
- }
- else {
- sender_packet_count = sp->peer_packet_count;
- receiver_packet_count = sp->packet_count;
- }
-
- if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits) {
- total_retransmits += sp->result->stream_retrans;
- }
- } else {
- /*
- * Running total of the total number of packets. Use the sender packet count if we
- * have it, otherwise use the receiver packet count.
- */
- int packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
- total_packets += (packet_count - sp->omitted_packet_count);
- sender_total_packets += (sender_packet_count - sp->omitted_packet_count);
- receiver_total_packets += (receiver_packet_count - sp->omitted_packet_count);
- lost_packets += (sp->cnt_error - sp->omitted_cnt_error);
- avg_jitter += sp->jitter;
+ if (sp->sender) {
+ sender_packet_count = sp->packet_count;
+ receiver_packet_count = sp->peer_packet_count;
+ }
+ else {
+ sender_packet_count = sp->peer_packet_count;
+ receiver_packet_count = sp->packet_count;
+ }
+
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits) {
+ total_retransmits += sp->result->stream_retrans;
+ }
+ } else {
+ /*
+ * Running total of the total number of packets. Use the sender packet count if we
+ * have it, otherwise use the receiver packet count.
+ */
+ int packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
+ total_packets += (packet_count - sp->omitted_packet_count);
+ sender_total_packets += (sender_packet_count - sp->omitted_packet_count);
+ receiver_total_packets += (receiver_packet_count - sp->omitted_packet_count);
+ lost_packets += (sp->cnt_error - sp->omitted_cnt_error);
+ avg_jitter += sp->jitter;
+ }
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A');
+ if (sender_time > 0.0) {
+ bandwidth = (double) bytes_sent / (double) sender_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits) {
+ /* Sender summary, TCP and SCTP with retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt), stream_must_be_sender));
+ else
+ if (test->role == 's' && !sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_retrans_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, sp->result->stream_retrans, report_sender);
+ }
+ } else {
+ /* Sender summary, TCP and SCTP without retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, stream_must_be_sender));
+ else
+ if (test->role == 's' && !sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, report_sender);
+ }
+ }
+ } else {
+ /* Sender summary, UDP. */
+ if (sender_packet_count - sp->omitted_packet_count > 0) {
+ lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (sender_packet_count - sp->omitted_packet_count);
+ }
+ else {
+ lost_percent = 0.0;
+ }
+ if (test->json_output) {
+ /*
+ * For hysterical raisins, we only emit one JSON
+ * object for the UDP summary, and it contains
+ * information for both the sender and receiver
+ * side.
+ *
+ * The JSON format as currently defined only includes one
+ * value for the number of packets. We usually want that
+ * to be the sender's value (how many packets were sent
+ * by the sender). However this value might not be
+ * available on the receiver in certain circumstances
+ * specifically on the server side for a normal test or
+ * the client side for a reverse-mode test. If this
+ * is the case, then use the receiver's count of packets
+ * instead.
+ */
+ int packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
+ cJSON_AddItemToObject(json_summary_stream, "udp", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f out_of_order: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (double) sp->jitter * 1000.0, (int64_t) (sp->cnt_error - sp->omitted_cnt_error), (int64_t) (packet_count - sp->omitted_packet_count), (double) lost_percent, (int64_t) (sp->outoforder_packets - sp->omitted_outoforder_packets), stream_must_be_sender));
+ }
+ else {
+ /*
+ * Due to ordering of messages on the control channel,
+ * the server cannot report on client-side summary
+ * statistics. If we're the server, omit one set of
+ * summary statistics to avoid giving meaningless
+ * results.
+ */
+ if (test->role == 's' && !sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, 0, (sender_packet_count - sp->omitted_packet_count), (double) 0, report_sender);
+ }
+ if ((sp->outoforder_packets - sp->omitted_outoforder_packets) > 0)
+ iperf_printf(test, report_sum_outoforder, mbuf, start_time, sender_time, (sp->outoforder_packets - sp->omitted_outoforder_packets));
+ }
+ }
+
+ if (sp->diskfile_fd >= 0) {
+ if (fstat(sp->diskfile_fd, &sb) == 0) {
+ /* In the odd case that it's a zero-sized file, say it was all transferred. */
+ int percent_sent = 100, percent_received = 100;
+ if (sb.st_size > 0) {
+ percent_sent = (int) ( ( (double) bytes_sent / (double) sb.st_size ) * 100.0 );
+ percent_received = (int) ( ( (double) bytes_received / (double) sb.st_size ) * 100.0 );
+ }
+ unit_snprintf(sbuf, UNIT_LEN, (double) sb.st_size, 'A');
+ if (test->json_output)
+ cJSON_AddItemToObject(json_summary_stream, "diskfile", iperf_json_printf("sent: %d received: %d size: %d percent_sent: %d percent_received: %d filename: %s", (int64_t) bytes_sent, (int64_t) bytes_received, (int64_t) sb.st_size, (int64_t) percent_sent, (int64_t) percent_received, test->diskfile_name));
+ else
+ if (stream_must_be_sender) {
+ iperf_printf(test, report_diskfile, ubuf, sbuf, percent_sent, test->diskfile_name);
+ }
+ else {
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
+ iperf_printf(test, report_diskfile, ubuf, sbuf, percent_received, test->diskfile_name);
+ }
+ }
+ }
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
+ if (receiver_time > 0) {
+ bandwidth = (double) bytes_received / (double) receiver_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ /* Receiver summary, TCP and SCTP */
+ if (test->json_output)
+ cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) receiver_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8, stream_must_be_sender));
+ else
+ if (test->role == 's' && sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_receiver_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, report_receiver);
+ }
+ }
+ else {
+ /*
+ * Receiver summary, UDP. Note that JSON was emitted with
+ * the sender summary, so we only deal with human-readable
+ * data here.
+ */
+ if (! test->json_output) {
+ if (receiver_packet_count - sp->omitted_packet_count > 0) {
+ lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (receiver_packet_count - sp->omitted_packet_count);
+ }
+ else {
+ lost_percent = 0.0;
+ }
+
+ if (test->role == 's' && sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_receiver_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (receiver_packet_count - sp->omitted_packet_count), lost_percent, report_receiver);
+ }
+ }
+ }
+ }
+ }
}
- unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A');
- if (sender_time > 0.0) {
- bandwidth = (double) bytes_sent / (double) sender_time;
- }
- else {
- bandwidth = 0.0;
- }
- unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
- if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits) {
- /* Sender summary, TCP and SCTP with retransmits. */
- if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt)));
- else
- if (test->role == 's' && !test->sender) {
- if (test->verbose)
- iperf_printf(test, report_sender_not_available_format, sp->socket);
- }
- else {
- iperf_printf(test, report_bw_retrans_format, sp->socket, start_time, sender_time, ubuf, nbuf, sp->result->stream_retrans, report_sender);
- }
- } else {
- /* Sender summary, TCP and SCTP without retransmits. */
- if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8));
- else
- if (test->role == 's' && !test->sender) {
- if (test->verbose)
- iperf_printf(test, report_sender_not_available_format, sp->socket);
- }
- else {
- iperf_printf(test, report_bw_format, sp->socket, start_time, sender_time, ubuf, nbuf, report_sender);
- }
- }
- } else {
- /* Sender summary, UDP. */
- if (sender_packet_count - sp->omitted_packet_count > 0) {
- lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (sender_packet_count - sp->omitted_packet_count);
- }
- else {
- lost_percent = 0.0;
- }
- if (test->json_output) {
- /*
- * For hysterical raisins, we only emit one JSON
- * object for the UDP summary, and it contains
- * information for both the sender and receiver
- * side.
- *
- * The JSON format as currently defined only includes one
- * value for the number of packets. We usually want that
- * to be the sender's value (how many packets were sent
- * by the sender). However this value might not be
- * available on the receiver in certain circumstances
- * specifically on the server side for a normal test or
- * the client side for a reverse-mode test. If this
- * is the case, then use the receiver's count of packets
- * instead.
- */
- int packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
- cJSON_AddItemToObject(json_summary_stream, "udp", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f out_of_order: %d", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (double) sp->jitter * 1000.0, (int64_t) (sp->cnt_error - sp->omitted_cnt_error), (int64_t) (packet_count - sp->omitted_packet_count), (double) lost_percent, (int64_t) (sp->outoforder_packets - sp->omitted_outoforder_packets)));
- }
- else {
- /*
- * Due to ordering of messages on the control channel,
- * the server cannot report on client-side summary
- * statistics. If we're the server, omit one set of
- * summary statistics to avoid giving meaningless
- * results.
- */
- if (test->role == 's' && !test->sender) {
- if (test->verbose)
- iperf_printf(test, report_sender_not_available_format, sp->socket);
- }
- else {
- iperf_printf(test, report_bw_udp_format, sp->socket, start_time, sender_time, ubuf, nbuf, 0.0, 0, (sender_packet_count - sp->omitted_packet_count), (double) 0, report_sender);
- }
- if ((sp->outoforder_packets - sp->omitted_outoforder_packets) > 0)
- iperf_printf(test, report_sum_outoforder, start_time, sender_time, (sp->outoforder_packets - sp->omitted_outoforder_packets));
- }
- }
-
- if (sp->diskfile_fd >= 0) {
- if (fstat(sp->diskfile_fd, &sb) == 0) {
- /* In the odd case that it's a zero-sized file, say it was all transferred. */
- int percent_sent = 100, percent_received = 100;
- if (sb.st_size > 0) {
- percent_sent = (int) ( ( (double) bytes_sent / (double) sb.st_size ) * 100.0 );
- percent_received = (int) ( ( (double) bytes_received / (double) sb.st_size ) * 100.0 );
- }
- unit_snprintf(sbuf, UNIT_LEN, (double) sb.st_size, 'A');
- if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "diskfile", iperf_json_printf("sent: %d received: %d size: %d percent_sent: %d percent_received: %d filename: %s", (int64_t) bytes_sent, (int64_t) bytes_received, (int64_t) sb.st_size, (int64_t) percent_sent, (int64_t) percent_received, test->diskfile_name));
- else
- if (test->sender) {
- iperf_printf(test, report_diskfile, ubuf, sbuf, percent_sent, test->diskfile_name);
- }
- else {
- unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
- iperf_printf(test, report_diskfile, ubuf, sbuf, percent_received, test->diskfile_name);
- }
- }
- }
-
- unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
- if (receiver_time > 0) {
- bandwidth = (double) bytes_received / (double) receiver_time;
- }
- else {
- bandwidth = 0.0;
- }
- unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
- if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- /* Receiver summary, TCP and SCTP */
- if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) receiver_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8));
- else
- if (test->role == 's' && test->sender) {
- if (test->verbose)
- iperf_printf(test, report_receiver_not_available_format, sp->socket);
- }
- else {
- iperf_printf(test, report_bw_format, sp->socket, start_time, receiver_time, ubuf, nbuf, report_receiver);
- }
- }
- else {
- /*
- * Receiver summary, UDP. Note that JSON was emitted with
- * the sender summary, so we only deal with human-readable
- * data here.
- */
- if (! test->json_output) {
- if (receiver_packet_count - sp->omitted_packet_count > 0) {
- lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (receiver_packet_count - sp->omitted_packet_count);
- }
- else {
- lost_percent = 0.0;
- }
-
- if (test->role == 's' && test->sender) {
- if (test->verbose)
- iperf_printf(test, report_receiver_not_available_format, sp->socket);
- }
- else {
- iperf_printf(test, report_bw_udp_format, sp->socket, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (receiver_packet_count - sp->omitted_packet_count), lost_percent, report_receiver);
- }
- }
- }
- }
- }
-
- if (test->num_streams > 1 || test->json_output) {
- unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
- /* If no tests were run, arbitrarily set bandwidth to 0. */
- if (sender_time > 0.0) {
- bandwidth = (double) total_sent / (double) sender_time;
- }
- else {
- bandwidth = 0.0;
- }
- unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
- if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits) {
- /* Summary sum, TCP with retransmits. */
- if (test->json_output)
- cJSON_AddItemToObject(test->json_end, "sum_sent", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, bandwidth * 8, (int64_t) total_retransmits));
- else
- if (test->role == 's' && !test->sender) {
- if (test->verbose)
- iperf_printf(test, report_sender_not_available_summary_format, "SUM");
- }
- else {
- iperf_printf(test, report_sum_bw_retrans_format, start_time, sender_time, ubuf, nbuf, total_retransmits, report_sender);
- }
- } else {
- /* Summary sum, TCP without retransmits. */
- if (test->json_output)
- cJSON_AddItemToObject(test->json_end, "sum_sent", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, bandwidth * 8));
- else
- if (test->role == 's' && !test->sender) {
- if (test->verbose)
- iperf_printf(test, report_sender_not_available_summary_format, "SUM");
- }
- else {
- iperf_printf(test, report_sum_bw_format, start_time, sender_time, ubuf, nbuf, report_sender);
- }
- }
- unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
- /* If no tests were run, set received bandwidth to 0 */
- if (receiver_time > 0.0) {
- bandwidth = (double) total_received / (double) receiver_time;
- }
- else {
- bandwidth = 0.0;
- }
+ if (test->num_streams > 1 || test->json_output) {
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
+ /* If no tests were run, arbitrarily set bandwidth to 0. */
+ if (sender_time > 0.0) {
+ bandwidth = (double) total_sent / (double) sender_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
- if (test->json_output)
- cJSON_AddItemToObject(test->json_end, "sum_received", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_received, bandwidth * 8));
- else
- if (test->role == 's' && test->sender) {
- if (test->verbose)
- iperf_printf(test, report_receiver_not_available_summary_format, "SUM");
- }
- else {
- iperf_printf(test, report_sum_bw_format, start_time, receiver_time, ubuf, nbuf, report_receiver);
- }
- } else {
- /* Summary sum, UDP. */
- avg_jitter /= test->num_streams;
- /* If no packets were sent, arbitrarily set loss percentage to 0. */
- if (total_packets > 0) {
- lost_percent = 100.0 * lost_packets / total_packets;
- }
- else {
- lost_percent = 0.0;
- }
- if (test->json_output)
- cJSON_AddItemToObject(test->json_end, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_sent, bandwidth * 8, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) total_packets, (double) lost_percent));
- else {
- /*
- * On the client we have both sender and receiver overall summary
- * stats. On the server we have only the side that was on the
- * server. Output whatever we have.
- */
- if (! (test->role == 's' && !test->sender) ) {
- unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
- iperf_printf(test, report_sum_bw_udp_format, start_time, sender_time, ubuf, nbuf, 0.0, 0, sender_total_packets, 0.0, "sender");
- }
- if (! (test->role == 's' && test->sender) ) {
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits) {
+ /* Summary sum, TCP with retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, "sum_sent", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d sender: %b", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, bandwidth * 8, (int64_t) total_retransmits, stream_must_be_sender));
+ else
+ if (test->role == 's' && !stream_must_be_sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_summary_format, "SUM");
+ }
+ else {
+ iperf_printf(test, report_sum_bw_retrans_format, mbuf, start_time, sender_time, ubuf, nbuf, total_retransmits, report_sender);
+ }
+ } else {
+ /* Summary sum, TCP without retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, "sum_sent", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, bandwidth * 8, stream_must_be_sender));
+ else
+ if (test->role == 's' && !stream_must_be_sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_summary_format, "SUM");
+ }
+ else {
+ iperf_printf(test, report_sum_bw_format, mbuf, start_time, sender_time, ubuf, nbuf, report_sender);
+ }
+ }
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
+ /* If no tests were run, set received bandwidth to 0 */
+ if (receiver_time > 0.0) {
+ bandwidth = (double) total_received / (double) receiver_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, "sum_received", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_received, bandwidth * 8, stream_must_be_sender));
+ else
+ if (test->role == 's' && stream_must_be_sender) {
+ if (test->verbose)
+ iperf_printf(test, report_receiver_not_available_summary_format, "SUM");
+ }
+ else {
+ iperf_printf(test, report_sum_bw_format, mbuf, start_time, receiver_time, ubuf, nbuf, report_receiver);
+ }
+ } else {
+ /* Summary sum, UDP. */
+ avg_jitter /= test->num_streams;
+ /* If no packets were sent, arbitrarily set loss percentage to 0. */
+ if (total_packets > 0) {
+ lost_percent = 100.0 * lost_packets / total_packets;
+ }
+ else {
+ lost_percent = 0.0;
+ }
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, "sum", iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f sender: %b", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_sent, bandwidth * 8, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) total_packets, (double) lost_percent, stream_must_be_sender));
+ else {
+ /*
+ * On the client we have both sender and receiver overall summary
+ * stats. On the server we have only the side that was on the
+ * server. Output whatever we have.
+ */
+ if (! (test->role == 's' && !stream_must_be_sender) ) {
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
+ iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, 0, sender_total_packets, 0.0, "sender");
+ }
+ if (! (test->role == 's' && stream_must_be_sender) ) {
- unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
- /* Compute received bandwidth. */
- if (end_time > 0.0) {
- bandwidth = (double) total_received / (double) receiver_time;
- }
- else {
- bandwidth = 0.0;
- }
- unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
- iperf_printf(test, report_sum_bw_udp_format, start_time, receiver_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, receiver_total_packets, lost_percent, "receiver");
- }
- }
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
+ /* Compute received bandwidth. */
+ if (end_time > 0.0) {
+ bandwidth = (double) total_received / (double) receiver_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, receiver_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, receiver_total_packets, lost_percent, "receiver");
+ }
+ }
+ }
+ }
+
+ if (test->json_output && current_mode == upper_mode) {
+ cJSON_AddItemToObject(test->json_end, "cpu_utilization_percent", iperf_json_printf("host_total: %f host_user: %f host_system: %f remote_total: %f remote_user: %f remote_system: %f", (double) test->cpu_util[0], (double) test->cpu_util[1], (double) test->cpu_util[2], (double) test->remote_cpu_util[0], (double) test->remote_cpu_util[1], (double) test->remote_cpu_util[2]));
+ if (test->protocol->id == Ptcp) {
+ char *snd_congestion = NULL, *rcv_congestion = NULL;
+ if (stream_must_be_sender) {
+ snd_congestion = test->congestion_used;
+ rcv_congestion = test->remote_congestion_used;
+ }
+ else {
+ snd_congestion = test->remote_congestion_used;
+ rcv_congestion = test->congestion_used;
+ }
+ if (snd_congestion) {
+ cJSON_AddStringToObject(test->json_end, "sender_tcp_congestion", snd_congestion);
+ }
+ if (rcv_congestion) {
+ cJSON_AddStringToObject(test->json_end, "receiver_tcp_congestion", rcv_congestion);
+ }
+ }
+ }
+ else {
+ if (test->verbose) {
+ if (stream_must_be_sender) {
+ if (test->bidirectional) {
+ iperf_printf(test, report_cpu, report_local, stream_must_be_sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, stream_must_be_sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
+ iperf_printf(test, report_cpu, report_local, !stream_must_be_sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, !stream_must_be_sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
+ } else
+ iperf_printf(test, report_cpu, report_local, stream_must_be_sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, stream_must_be_sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
+ }
+ if (test->protocol->id == Ptcp) {
+ char *snd_congestion = NULL, *rcv_congestion = NULL;
+ if (stream_must_be_sender) {
+ snd_congestion = test->congestion_used;
+ rcv_congestion = test->remote_congestion_used;
+ }
+ else {
+ snd_congestion = test->remote_congestion_used;
+ rcv_congestion = test->congestion_used;
+ }
+ if (snd_congestion) {
+ iperf_printf(test, "snd_tcp_congestion %s\n", snd_congestion);
+ }
+ if (rcv_congestion) {
+ iperf_printf(test, "rcv_tcp_congestion %s\n", rcv_congestion);
+ }
+ }
+ }
+
+ /* Print server output if we're on the client and it was requested/provided */
+ if (test->role == 'c' && iperf_get_test_get_server_output(test)) {
+ if (test->json_server_output) {
+ iperf_printf(test, "\nServer JSON output:\n%s\n", cJSON_Print(test->json_server_output));
+ cJSON_Delete(test->json_server_output);
+ test->json_server_output = NULL;
+ }
+ if (test->server_output_text) {
+ iperf_printf(test, "\nServer output:\n%s\n", test->server_output_text);
+ test->server_output_text = NULL;
+ }
+ }
}
}
- if (test->json_output) {
- cJSON_AddItemToObject(test->json_end, "cpu_utilization_percent", iperf_json_printf("host_total: %f host_user: %f host_system: %f remote_total: %f remote_user: %f remote_system: %f", (double) test->cpu_util[0], (double) test->cpu_util[1], (double) test->cpu_util[2], (double) test->remote_cpu_util[0], (double) test->remote_cpu_util[1], (double) test->remote_cpu_util[2]));
- if (test->protocol->id == Ptcp) {
- char *snd_congestion = NULL, *rcv_congestion = NULL;
- if (test->sender) {
- snd_congestion = test->congestion_used;
- rcv_congestion = test->remote_congestion_used;
- }
- else {
- snd_congestion = test->remote_congestion_used;
- rcv_congestion = test->congestion_used;
- }
- if (snd_congestion) {
- cJSON_AddStringToObject(test->json_end, "sender_tcp_congestion", snd_congestion);
- }
- if (rcv_congestion) {
- cJSON_AddStringToObject(test->json_end, "receiver_tcp_congestion", rcv_congestion);
- }
- }
- }
- else {
- if (test->verbose) {
- iperf_printf(test, report_cpu, report_local, test->sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, test->sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
-
- if (test->protocol->id == Ptcp) {
- char *snd_congestion = NULL, *rcv_congestion = NULL;
- if (test->sender) {
- snd_congestion = test->congestion_used;
- rcv_congestion = test->remote_congestion_used;
- }
- else {
- snd_congestion = test->remote_congestion_used;
- rcv_congestion = test->congestion_used;
- }
- if (snd_congestion) {
- iperf_printf(test, "snd_tcp_congestion %s\n", snd_congestion);
- }
- if (rcv_congestion) {
- iperf_printf(test, "rcv_tcp_congestion %s\n", rcv_congestion);
- }
- }
- }
-
- /* Print server output if we're on the client and it was requested/provided */
- if (test->role == 'c' && iperf_get_test_get_server_output(test)) {
- if (test->json_server_output) {
- iperf_printf(test, "\nServer JSON output:\n%s\n", cJSON_Print(test->json_server_output));
- cJSON_Delete(test->json_server_output);
- test->json_server_output = NULL;
- }
- if (test->server_output_text) {
- iperf_printf(test, "\nServer output:\n%s\n", test->server_output_text);
- test->server_output_text = NULL;
- }
- }
- }
+ /* Set real sender_has_retransmits for current side */
+ if (test->mode == BIDIRECTIONAL)
+ test->sender_has_retransmits = tmp_sender_has_retransmits;
}
/**************************************************************************/
@@ -3212,11 +3380,20 @@
char ubuf[UNIT_LEN];
char nbuf[UNIT_LEN];
char cbuf[UNIT_LEN];
+ char mbuf[UNIT_LEN];
+ char zbuf[] = " ";
double st = 0., et = 0.;
struct iperf_time temp_time;
struct iperf_interval_results *irp = NULL;
double bandwidth, lost_percent;
+ if (test->mode == BIDIRECTIONAL) {
+ sprintf(mbuf, "[%s-%s]", sp->sender?"TX":"RX", test->role == 'c'?"C":"S");
+ } else {
+ mbuf[0] = '\0';
+ zbuf[0] = '\0';
+ }
+
irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); /* get last entry in linked list */
if (irp == NULL) {
iperf_err(test, "print_interval_results error: interval_results is NULL");
@@ -3231,15 +3408,27 @@
*/
if (iperf_time_compare(&sp->result->start_time, &irp->interval_start_time) == 0) {
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits == 1)
- iperf_printf(test, "%s", report_bw_retrans_cwnd_header);
- else
- iperf_printf(test, "%s", report_bw_header);
+ if (test->sender_has_retransmits == 1) {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_retrans_cwnd_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_retrans_cwnd_header);
+ }
+ else {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_header);
+ }
} else {
- if (test->sender)
- iperf_printf(test, "%s", report_bw_udp_sender_header);
- else
- iperf_printf(test, "%s", report_bw_udp_header);
+ if (test->mode == SENDER) {
+ iperf_printf(test, "%s", report_bw_udp_sender_header);
+ } else if (test->mode == RECEIVER){
+ iperf_printf(test, "%s", report_bw_udp_header);
+ } else {
+ /* BIDIRECTIONAL */
+ iperf_printf(test, "%s", report_bw_udp_header_bidir);
+ }
}
} else if (test->num_streams > 1)
iperf_printf(test, "%s", report_bw_separator);
@@ -3261,28 +3450,28 @@
et = iperf_time_in_secs(&temp_time);
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits == 1) {
+ if (test->sender_has_retransmits == 1 && sp->sender) {
/* Interval, TCP with retransmits. */
if (test->json_output)
- cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d rtt: %d rttvar: %d pmtu: %d omitted: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_retrans, (int64_t) irp->snd_cwnd, (int64_t) irp->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, irp->omitted));
+ cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d rtt: %d rttvar: %d pmtu: %d omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_retrans, (int64_t) irp->snd_cwnd, (int64_t) irp->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, irp->omitted, sp->sender));
else {
unit_snprintf(cbuf, UNIT_LEN, irp->snd_cwnd, 'A');
- iperf_printf(test, report_bw_retrans_cwnd_format, sp->socket, st, et, ubuf, nbuf, irp->interval_retrans, cbuf, irp->omitted?report_omitted:"");
+ iperf_printf(test, report_bw_retrans_cwnd_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->interval_retrans, cbuf, irp->omitted?report_omitted:"");
}
} else {
/* Interval, TCP without retransmits. */
if (test->json_output)
- cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f omitted: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, irp->omitted));
+ cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, irp->omitted, sp->sender));
else
- iperf_printf(test, report_bw_format, sp->socket, st, et, ubuf, nbuf, irp->omitted?report_omitted:"");
+ iperf_printf(test, report_bw_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->omitted?report_omitted:"");
}
} else {
/* Interval, UDP. */
- if (test->sender) {
+ if (sp->sender) {
if (test->json_output)
- cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f packets: %d omitted: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_packet_count, irp->omitted));
+ cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f packets: %d omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_packet_count, irp->omitted, sp->sender));
else
- iperf_printf(test, report_bw_udp_sender_format, sp->socket, st, et, ubuf, nbuf, irp->interval_packet_count, irp->omitted?report_omitted:"");
+ iperf_printf(test, report_bw_udp_sender_format, sp->socket, mbuf, st, et, ubuf, nbuf, zbuf, irp->interval_packet_count, irp->omitted?report_omitted:"");
} else {
if (irp->interval_packet_count > 0) {
lost_percent = 100.0 * irp->interval_cnt_error / irp->interval_packet_count;
@@ -3291,9 +3480,9 @@
lost_percent = 0.0;
}
if (test->json_output)
- cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f omitted: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (double) irp->jitter * 1000.0, (int64_t) irp->interval_cnt_error, (int64_t) irp->interval_packet_count, (double) lost_percent, irp->omitted));
+ cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (double) irp->jitter * 1000.0, (int64_t) irp->interval_cnt_error, (int64_t) irp->interval_packet_count, (double) lost_percent, irp->omitted, sp->sender));
else
- iperf_printf(test, report_bw_udp_format, sp->socket, st, et, ubuf, nbuf, irp->jitter * 1000.0, irp->interval_cnt_error, irp->interval_packet_count, lost_percent, irp->omitted?report_omitted:"");
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->jitter * 1000.0, irp->interval_cnt_error, irp->interval_packet_count, lost_percent, irp->omitted?report_omitted:"");
}
}
@@ -3324,7 +3513,7 @@
/**************************************************************************/
struct iperf_stream *
-iperf_new_stream(struct iperf_test *test, int s)
+iperf_new_stream(struct iperf_test *test, int s, int sender)
{
struct iperf_stream *sp;
int ret = 0;
@@ -3355,6 +3544,7 @@
memset(sp, 0, sizeof(struct iperf_stream));
+ sp->sender = sender;
sp->test = test;
sp->settings = test->settings;
sp->result = (struct iperf_stream_result *) malloc(sizeof(struct iperf_stream_result));
@@ -3402,7 +3592,7 @@
sp->rcv = test->protocol->recv;
if (test->diskfile_name != (char*) 0) {
- sp->diskfile_fd = open(test->diskfile_name, test->sender ? O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), S_IRUSR|S_IWUSR);
+ sp->diskfile_fd = open(test->diskfile_name, sender ? O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), S_IRUSR|S_IWUSR);
if (sp->diskfile_fd == -1) {
i_errno = IEFILE;
munmap(sp->buffer, sp->test->settings->blksize);
diff --git a/src/iperf_api.h b/src/iperf_api.h
index 54936ab..eba7e70 100755
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -71,6 +71,7 @@
#define OPT_CONNECT_TIMEOUT 17
#define OPT_REPEATING_PAYLOAD 18
#define OPT_EXTRA_DATA 19
+#define OPT_BIDIRECTIONAL 20
/* states */
#define TEST_START 1
@@ -155,6 +156,7 @@
void iperf_set_test_one_off( struct iperf_test* ipt, int one_off );
void iperf_set_test_tos( struct iperf_test* ipt, int tos );
void iperf_set_extra_data( struct iperf_test* ipt, char *dat);
+void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional);
#if defined(HAVE_SSL)
void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username);
@@ -216,7 +218,7 @@
* returns NULL on failure
*
*/
-struct iperf_stream *iperf_new_stream(struct iperf_test *, int);
+struct iperf_stream *iperf_new_stream(struct iperf_test *, int, int);
/**
* iperf_add_stream -- add a stream to a test
@@ -276,7 +278,7 @@
/* Client routines. */
int iperf_run_client(struct iperf_test *);
int iperf_connect(struct iperf_test *);
-int iperf_create_streams(struct iperf_test *);
+int iperf_create_streams(struct iperf_test *, int sender);
int iperf_handle_message_client(struct iperf_test *);
int iperf_client_end(struct iperf_test *);
@@ -332,6 +334,7 @@
IESETCLIENTAUTH = 22, // Bad configuration of client authentication
IESETSERVERAUTH = 23, // Bad configuration of server authentication
IEBADFORMAT = 24, // Bad format argument to -f
+ IEREVERSEBIDIR = 25, // Iperf cannot be both reverse and bidirectional
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c
index aca6826..46c283b 100644
--- a/src/iperf_client_api.c
+++ b/src/iperf_client_api.c
@@ -52,7 +52,7 @@
#endif /* HAVE_TCP_CONGESTION */
int
-iperf_create_streams(struct iperf_test *test)
+iperf_create_streams(struct iperf_test *test, int sender)
{
int i, s;
#if defined(HAVE_TCP_CONGESTION)
@@ -98,13 +98,13 @@
}
#endif /* HAVE_TCP_CONGESTION */
- if (test->sender)
+ if (sender)
FD_SET(s, &test->write_set);
else
FD_SET(s, &test->read_set);
if (s > test->max_fd) test->max_fd = s;
- sp = iperf_new_stream(test, s);
+ sp = iperf_new_stream(test, s, sender);
if (!sp)
return -1;
@@ -252,7 +252,14 @@
test->on_connect(test);
break;
case CREATE_STREAMS:
- if (iperf_create_streams(test) < 0)
+ if (test->mode == BIDIRECTIONAL)
+ {
+ if (iperf_create_streams(test, 1) < 0)
+ return -1;
+ if (iperf_create_streams(test, 0) < 0)
+ return -1;
+ }
+ else if (iperf_create_streams(test, test->mode) < 0)
return -1;
break;
case TEST_START:
@@ -262,7 +269,7 @@
return -1;
if (create_client_omit_timer(test) < 0)
return -1;
- if (!test->reverse)
+ if (test->mode)
if (iperf_create_send_timers(test) < 0)
return -1;
break;
@@ -506,16 +513,24 @@
}
}
- if (test->reverse) {
- // Reverse mode. Client receives.
- if (iperf_recv(test, &read_set) < 0)
- return -1;
+
+ if (test->mode == BIDIRECTIONAL)
+ {
+ if (iperf_send(test, &write_set) < 0)
+ return -1;
+ if (iperf_recv(test, &read_set) < 0)
+ return -1;
+ } else if (test->mode == SENDER) {
+ // Regular mode. Client sends.
+ if (iperf_send(test, &write_set) < 0)
+ return -1;
} else {
- // Regular mode. Client sends.
- if (iperf_send(test, &write_set) < 0)
- return -1;
+ // Reverse mode. Client receives.
+ if (iperf_recv(test, &read_set) < 0)
+ return -1;
}
+
/* Run the timers. */
iperf_time_now(&now);
tmr_run(&now);
@@ -546,7 +561,7 @@
// deadlock where the server side fills up its pipe(s)
// and gets blocked, so it can't receive state changes
// from the client side.
- else if (test->reverse && test->state == TEST_END) {
+ else if (test->mode == RECEIVER && test->state == TEST_END) {
if (iperf_recv(test, &read_set) < 0)
return -1;
}
diff --git a/src/iperf_error.c b/src/iperf_error.c
index 945984e..3d34b63 100644
--- a/src/iperf_error.c
+++ b/src/iperf_error.c
@@ -374,6 +374,9 @@
case IESETBUF2:
snprintf(errstr, len, "socket buffer size not set correctly");
break;
+ case IEREVERSEBIDIR:
+ snprintf(errstr, len, "cannot be both reverse and bidirectional");
+ break;
}
diff --git a/src/iperf_locale.c b/src/iperf_locale.c
index 115adf2..0d8a7ec 100644
--- a/src/iperf_locale.c
+++ b/src/iperf_locale.c
@@ -148,6 +148,8 @@
" --cport <port> bind to a specific client port (TCP and UDP, default: ephemeral port)\n"
" -P, --parallel # number of parallel client streams to run\n"
" -R, --reverse run in reverse mode (server sends, client receives)\n"
+ " --bidir run in bidirectional mode.\n"
+ " Client and server send and receive data.\n"
" -w, --window #[KMG] set window size / socket buffer size\n"
#if defined(HAVE_TCP_CONGESTION)
" -C, --congestion <algo> set TCP congestion control algorithm (Linux and FreeBSD only)\n"
@@ -312,47 +314,62 @@
const char report_bw_header[] =
"[ ID] Interval Transfer Bitrate\n";
+const char report_bw_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate\n";
+
const char report_bw_retrans_header[] =
"[ ID] Interval Transfer Bitrate Retr\n";
+const char report_bw_retrans_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Retr\n";
+
const char report_bw_retrans_cwnd_header[] =
"[ ID] Interval Transfer Bitrate Retr Cwnd\n";
+const char report_bw_retrans_cwnd_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Retr Cwnd\n";
+
const char report_bw_udp_header[] =
"[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams\n";
+const char report_bw_udp_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Jitter Lost/Total Datagrams\n";
+
const char report_bw_udp_sender_header[] =
"[ ID] Interval Transfer Bitrate Total Datagrams\n";
+const char report_bw_udp_sender_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Total Datagrams\n";
+
const char report_bw_format[] =
-"[%3d] %6.2f-%-6.2f sec %ss %ss/sec %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s\n";
const char report_bw_retrans_format[] =
-"[%3d] %6.2f-%-6.2f sec %ss %ss/sec %3u %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3u %s\n";
const char report_bw_retrans_cwnd_format[] =
-"[%3d] %6.2f-%-6.2f sec %ss %ss/sec %3u %ss %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3u %ss %s\n";
const char report_bw_udp_format[] =
-"[%3d] %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %d/%d (%.2g%%) %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %d/%d (%.2g%%) %s\n";
const char report_bw_udp_sender_format[] =
-"[%3d] %6.2f-%-6.2f sec %ss %ss/sec %d %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s %d %s\n";
const char report_summary[] =
"Test Complete. Summary Results:\n";
const char report_sum_bw_format[] =
-"[SUM] %6.2f-%-6.2f sec %ss %ss/sec %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s\n";
const char report_sum_bw_retrans_format[] =
-"[SUM] %6.2f-%-6.2f sec %ss %ss/sec %3d %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %3d %s\n";
const char report_sum_bw_udp_format[] =
-"[SUM] %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %d/%d (%.2g%%) %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %d/%d (%.2g%%) %s\n";
const char report_sum_bw_udp_sender_format[] =
-"[SUM] %6.2f-%-6.2f sec %ss %ss/sec %d %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s %d %s\n";
const char report_omitted[] = "(omitted)";
@@ -360,10 +377,10 @@
"- - - - - - - - - - - - - - - - - - - - - - - - -\n";
const char report_outoforder[] =
-"[%3d] %4.1f-%4.1f sec %d datagrams received out-of-order\n";
+"[%3d]%s %4.1f-%4.1f sec %d datagrams received out-of-order\n";
const char report_sum_outoforder[] =
-"[SUM] %4.1f-%4.1f sec %d datagrams received out-of-order\n";
+"[SUM]%s %4.1f-%4.1f sec %d datagrams received out-of-order\n";
const char report_peer[] =
"[%3d] local %s port %u connected with %s port %u\n";
diff --git a/src/iperf_locale.h b/src/iperf_locale.h
index e1ceaf7..ad784a6 100644
--- a/src/iperf_locale.h
+++ b/src/iperf_locale.h
@@ -64,10 +64,15 @@
extern const char report_read_lengths[] ;
extern const char report_read_length_times[] ;
extern const char report_bw_header[] ;
+extern const char report_bw_header_bidir[] ;
extern const char report_bw_retrans_header[] ;
+extern const char report_bw_retrans_header_bidir[] ;
extern const char report_bw_retrans_cwnd_header[] ;
+extern const char report_bw_retrans_cwnd_header_bidir[] ;
extern const char report_bw_udp_header[] ;
+extern const char report_bw_udp_header_bidir[] ;
extern const char report_bw_udp_sender_header[] ;
+extern const char report_bw_udp_sender_header_bidir[] ;
extern const char report_bw_format[] ;
extern const char report_bw_retrans_format[] ;
extern const char report_bw_retrans_cwnd_format[] ;
diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c
index 78c7099..079da89 100644
--- a/src/iperf_server_api.c
+++ b/src/iperf_server_api.c
@@ -386,7 +386,9 @@
int
iperf_run_server(struct iperf_test *test)
{
- int result, s, streams_accepted;
+ int result, s;
+ int send_streams_accepted, rec_streams_accepted;
+ int streams_to_send = 0, streams_to_rec = 0;
#if defined(HAVE_TCP_CONGESTION)
int saved_errno;
#endif /* HAVE_TCP_CONGESTION */
@@ -394,6 +396,7 @@
struct iperf_stream *sp;
struct iperf_time now;
struct timeval* timeout;
+ int flag;
if (test->affinity != -1)
if (iperf_setaffinity(test, test->affinity) != 0)
@@ -422,7 +425,8 @@
cpu_util(NULL);
test->state = IPERF_START;
- streams_accepted = 0;
+ send_streams_accepted = 0;
+ rec_streams_accepted = 0;
while (test->state != IPERF_DONE) {
@@ -445,6 +449,18 @@
return -1;
}
FD_CLR(test->listener, &read_set);
+
+ // Set streams number
+ if (test->mode == BIDIRECTIONAL) {
+ streams_to_send = test->num_streams;
+ streams_to_rec = test->num_streams;
+ } else if (test->mode == RECEIVER) {
+ streams_to_rec = test->num_streams;
+ streams_to_send = 0;
+ } else {
+ streams_to_send = test->num_streams;
+ streams_to_rec = 0;
+ }
}
}
if (FD_ISSET(test->ctrl_sck, &read_set)) {
@@ -509,37 +525,51 @@
#endif /* HAVE_TCP_CONGESTION */
if (!is_closed(s)) {
- sp = iperf_new_stream(test, s);
- if (!sp) {
- cleanup_server(test);
- return -1;
- }
- if (test->sender)
- FD_SET(s, &test->write_set);
- else
- FD_SET(s, &test->read_set);
- if (s > test->max_fd) test->max_fd = s;
+ if (rec_streams_accepted != streams_to_rec) {
+ flag = 0;
+ ++rec_streams_accepted;
+ } else if (send_streams_accepted != streams_to_send) {
+ flag = 1;
+ ++send_streams_accepted;
+ }
- /*
- * If the protocol isn't UDP, or even if it is but
- * we're the receiver, set nonblocking sockets.
- * We need this to allow a server receiver to
- * maintain interactivity with the control channel.
- */
- if (test->protocol->id != Pudp ||
- !test->sender) {
- setnonblocking(s, 1);
- }
+ if (flag != -1) {
+ sp = iperf_new_stream(test, s, flag);
+ if (!sp) {
+ cleanup_server(test);
+ return -1;
+ }
- streams_accepted++;
- if (test->on_new_stream)
- test->on_new_stream(sp);
+ if (sp->sender)
+ FD_SET(s, &test->write_set);
+ else
+ FD_SET(s, &test->read_set);
+
+ if (s > test->max_fd) test->max_fd = s;
+
+ /*
+ * If the protocol isn't UDP, or even if it is but
+ * we're the receiver, set nonblocking sockets.
+ * We need this to allow a server receiver to
+ * maintain interactivity with the control channel.
+ */
+ if (test->protocol->id != Pudp ||
+ !sp->sender) {
+ setnonblocking(s, 1);
+ }
+
+ if (test->on_new_stream)
+ test->on_new_stream(sp);
+
+ flag = -1;
+ }
}
FD_CLR(test->prot_listener, &read_set);
}
- if (streams_accepted == test->num_streams) {
+
+ if (rec_streams_accepted == streams_to_rec && send_streams_accepted == streams_to_send) {
if (test->protocol->id != Ptcp) {
FD_CLR(test->prot_listener, &test->read_set);
close(test->prot_listener);
@@ -575,7 +605,7 @@
cleanup_server(test);
return -1;
}
- if (test->reverse)
+ if (test->mode != RECEIVER)
if (iperf_create_send_timers(test) < 0) {
cleanup_server(test);
return -1;
@@ -588,7 +618,16 @@
}
if (test->state == TEST_RUNNING) {
- if (test->reverse) {
+ if (test->mode == BIDIRECTIONAL) {
+ if (iperf_recv(test, &read_set) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ if (iperf_send(test, &write_set) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ } else if (test->mode == SENDER) {
// Reverse mode. Server sends.
if (iperf_send(test, &write_set) < 0) {
cleanup_server(test);