2 * libwebsockets-test-ping - libwebsockets test floodping
4 * Copyright (C) 2011-2016 Andy Green <andy@warmcat.com>
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
9 * The person who associated a work with this deed has dedicated
10 * the work to the public domain by waiving all of his or her rights
11 * to the work worldwide under copyright law, including all related
12 * and neighboring rights, to the extent allowed by law. You can copy,
13 * modify, distribute and perform the work, even for commercial purposes,
14 * all without asking permission.
16 * The test apps are intended to be adapted for use in your code, which
17 * may be proprietary. So unlike the library itself, they are licensed
26 #include <sys/types.h>
28 #include "../lib/libwebsockets.h"
32 #include <sys/socket.h>
34 #include <sys/ioctl.h>
38 #include "gettimeofday.h"
46 #include <sys/termios.h>
50 * this is specified in the 04 standard, control frames can only have small
51 * payload length styles
53 #define MAX_PING_PAYLOAD 125
54 #define MAX_MIRROR_PAYLOAD 4096
55 #define MAX_PING_CLIENTS 256
56 #define PING_RINGBUFFER_SIZE 256
58 static struct lws *ping_wsi[MAX_PING_CLIENTS];
59 static unsigned int interval_us = 1000000;
60 static unsigned int size = 64;
62 static const char *address;
63 static unsigned char pingbuf[LWS_PRE + MAX_MIRROR_PAYLOAD];
64 static char peer_name[128];
65 static unsigned long started;
66 static int screen_width = 80;
67 static int use_mirror;
68 static unsigned int write_options;
70 static unsigned long rtt_min = 100000000;
71 static unsigned long rtt_max;
72 static unsigned long rtt_avg;
73 static unsigned long global_rx_count;
74 static unsigned long global_tx_count;
75 static int clients = 1;
76 static unsigned long interrupted_time;
79 unsigned long issue_timestamp;
84 struct per_session_data__ping {
85 unsigned long long ping_index;
87 struct ping ringbuffer[PING_RINGBUFFER_SIZE];
91 unsigned long rx_count;
95 * uses the ping pong protocol features to provide an equivalent for the
96 * ping utility for 04+ websockets
109 callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
110 void *user, void *in, size_t len)
112 struct per_session_data__ping *psd = user;
117 unsigned long long l;
122 case LWS_CALLBACK_CLOSED:
123 fprintf(stderr, "LWS_CALLBACK_CLOSED on %p\n", (void *)wsi);
125 /* remove closed guy */
127 for (n = 0; n < clients; n++)
128 if (ping_wsi[n] == wsi) {
130 while (n < clients) {
131 ping_wsi[n] = ping_wsi[n + 1];
138 case LWS_CALLBACK_CLIENT_ESTABLISHED:
142 psd->ringbuffer_head = 0;
143 psd->ringbuffer_tail = 0;
146 * start the ball rolling,
147 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
150 lws_callback_on_writable(wsi);
153 case LWS_CALLBACK_CLIENT_RECEIVE:
154 case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
155 gettimeofday(&tv, NULL);
156 iv = (tv.tv_sec * 1000000) + tv.tv_usec;
165 l |= ((lws_intptr_t)*p++) << shift;
169 /* find it in the ringbuffer, look backwards from head */
170 n = psd->ringbuffer_head;
173 if (psd->ringbuffer[n].index == l) {
174 psd->ringbuffer[n].seen++;
179 if (n == psd->ringbuffer_tail) {
185 n = PING_RINGBUFFER_SIZE - 1;
193 fprintf(stderr, "%d bytes from %s: req=%ld "
194 "time=(unknown)\n", (int)len, address,
197 fprintf(stderr, "\b \b");
202 if (psd->ringbuffer[n].seen > 1)
203 fprintf(stderr, "DUP! ");
205 if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min)
206 rtt_min = iv - psd->ringbuffer[n].issue_timestamp;
208 if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max)
209 rtt_max = iv - psd->ringbuffer[n].issue_timestamp;
211 rtt_avg += iv - psd->ringbuffer[n].issue_timestamp;
215 fprintf(stderr, "%d bytes from %s: req=%ld "
216 "time=%lu.%lums\n", (int)len, address, (long)l,
217 (iv - psd->ringbuffer[n].issue_timestamp) / 1000,
218 ((iv - psd->ringbuffer[n].issue_timestamp) / 100) % 10);
220 fprintf(stderr, "\b \b");
223 case LWS_CALLBACK_CLIENT_WRITEABLE:
226 p = &pingbuf[LWS_PRE];
228 /* 64-bit ping index in network byte order */
231 *p++ = (unsigned char)(psd->ping_index >> shift);
235 while ((unsigned int)(p - &pingbuf[LWS_PRE]) < size)
238 gettimeofday(&tv, NULL);
240 psd->ringbuffer[psd->ringbuffer_head].issue_timestamp =
241 (tv.tv_sec * 1000000) + tv.tv_usec;
242 psd->ringbuffer[psd->ringbuffer_head].index = (unsigned long)psd->ping_index++;
243 psd->ringbuffer[psd->ringbuffer_head].seen = 0;
245 if (psd->ringbuffer_head == PING_RINGBUFFER_SIZE - 1)
246 psd->ringbuffer_head = 0;
248 psd->ringbuffer_head++;
250 /* snip any re-used tail so we keep to the ring length */
252 if (psd->ringbuffer_tail == psd->ringbuffer_head) {
253 if (psd->ringbuffer_tail == PING_RINGBUFFER_SIZE - 1)
254 psd->ringbuffer_tail = 0;
256 psd->ringbuffer_tail++;
264 size, write_options | LWS_WRITE_BINARY);
268 size, write_options | LWS_WRITE_PING);
273 lwsl_err("Partial write\n");
278 (psd->ping_index - psd->rx_count) < (screen_width - 1))
279 fprintf(stderr, ".");
290 /* list of supported protocols and callbacks */
292 static struct lws_protocols protocols[] = {
295 "lws-mirror-protocol",
297 sizeof (struct per_session_data__ping),
300 NULL, NULL, 0/* end of list */
304 static const struct lws_extension exts[] = {
306 "permessage-deflate",
307 lws_extension_callback_pm_deflate,
308 "permessage-deflate; client_no_context_takeover; client_max_window_bits"
312 lws_extension_callback_pm_deflate,
315 { NULL, NULL, NULL /* terminator */ }
318 static struct option options[] = {
319 { "help", no_argument, NULL, 'h' },
320 { "debug", required_argument, NULL, 'd' },
321 { "port", required_argument, NULL, 'p' },
322 { "ssl", no_argument, NULL, 't' },
323 { "interval", required_argument, NULL, 'i' },
324 { "size", required_argument, NULL, 's' },
325 { "protocol", required_argument, NULL, 'n' },
326 { "flood", no_argument, NULL, 'f' },
327 { "mirror", no_argument, NULL, 'm' },
328 { "replicate", required_argument, NULL, 'r' },
329 { "killmask", no_argument, NULL, 'k' },
330 { "version", required_argument, NULL, 'v' },
336 signal_handler(int sig, siginfo_t *si, void *v)
340 gettimeofday(&tv, NULL);
341 interrupted_time = (tv.tv_sec * 1000000) + tv.tv_usec;
345 int main(int argc, char **argv)
350 struct lws_context *context;
351 char protocol_name[256], ads_port[300];
358 unsigned long oldus = 0;
360 int ietf_version = -1;
361 struct lws_context_creation_info info;
362 struct lws_client_connect_info i;
364 memset(&info, 0, sizeof info);
370 n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:d:", options, NULL);
375 lws_set_log_level(atoi(optarg), NULL);
381 use_ssl = 2; /* 2 = allow selfsigned */
387 strncpy(protocol_name, optarg, sizeof protocol_name);
388 protocol_name[(sizeof protocol_name) - 1] = '\0';
389 protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
392 interval_us = (unsigned int)(1000000.0 * atof(optarg));
401 clients = atoi(optarg);
402 if (clients > MAX_PING_CLIENTS || clients < 1) {
403 fprintf(stderr, "Max clients supported = %d\n",
409 write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
412 ietf_version = atoi(optarg);
421 if (size > MAX_PING_PAYLOAD) {
422 fprintf(stderr, "Max ping opcode payload size %d\n",
427 if (size > MAX_MIRROR_PAYLOAD) {
428 fprintf(stderr, "Max mirror payload size %d\n",
435 if (isatty(STDOUT_FILENO))
436 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
438 screen_width = w.ws_col;
441 info.port = CONTEXT_PORT_NO_LISTEN;
442 info.protocols = protocols;
443 info.extensions = exts;
449 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
451 context = lws_create_context(&info);
452 if (context == NULL) {
453 fprintf(stderr, "Creating libwebsocket context failed\n");
457 /* create client websockets using dumb increment protocol */
459 address = argv[optind];
460 lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
461 address, port & 65535);
462 lwsl_notice("Connecting to %s...\n", ads_port);
463 memset(&i, 0, sizeof(i));
467 i.ssl_connection = use_ssl;
471 i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
472 i.ietf_version_or_minus_one = ietf_version;
474 for (n = 0; n < clients; n++) {
475 ping_wsi[n] = lws_client_connect_via_info(&i);
476 if (ping_wsi[n] == NULL) {
477 lwsl_err("client %d failed to connect\n", n);
482 lws_get_peer_addresses(ping_wsi[0], lws_get_socket_fd(ping_wsi[0]),
483 peer_name, sizeof peer_name, ip, sizeof ip);
485 lwsl_notice("libwebsockets test server ping - license LGPL2.1+SLE\n");
486 lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
487 fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n",
488 peer_name, ip, size);
491 /* set the ^C handler */
492 sa.sa_sigaction = signal_handler;
493 sa.sa_flags = SA_SIGINFO;
494 sigemptyset(&sa.sa_mask);
495 sigaction(SIGINT, &sa, NULL);
498 gettimeofday(&tv, NULL);
499 started = (tv.tv_sec * 1000000) + tv.tv_usec;
506 gettimeofday(&tv, NULL);
507 l = (tv.tv_sec * 1000000) + tv.tv_usec;
509 /* servers can hang up on us */
516 if (!interrupted_time) {
517 if ((l - oldus) > interval_us) {
518 for (n = 0; n < clients; n++)
519 lws_callback_on_writable(ping_wsi[n]);
524 /* allow time for in-flight pongs to come */
526 if ((l - interrupted_time) > 250000) {
532 n = lws_service(context, 0);
534 n = lws_service(context, 1);
539 fprintf(stderr, "\n--- %s websocket ping statistics "
540 "using %d connections ---\n"
541 "%lu packets transmitted, %lu received, "
542 "%lu%% packet loss, time %ldms\n"
543 "rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n"
544 "payload bandwidth average %0.3f KiBytes/sec\n",
545 peer_name, clients, global_tx_count, global_rx_count,
546 ((global_tx_count - global_rx_count) * 100) / global_tx_count,
547 (l - started) / 1000,
548 ((double)rtt_min) / 1000.0,
549 ((double)rtt_avg / global_rx_count) / 1000.0,
550 ((double)rtt_max) / 1000.0,
551 ((double)global_rx_count * (double)size) /
552 ((double)(l - started) / 1000000.0) / 1024.0);
554 lws_context_destroy(context);
559 fprintf(stderr, "Usage: libwebsockets-test-ping "
560 "<server address> [--port=<p>] "
561 "[--ssl] [--interval=<float sec>] "
563 "[--protocol=<protocolname>] "
565 "[--replicate=clients>] "
566 "[--version <version>] "
567 "[-d <log bitfield> ]"