2 * libwebsockets-test-ping - libwebsockets floodping
4 * Copyright (C) 2011 Andy Green <andy@warmcat.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation:
9 * version 2.1 of the License.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <sys/ioctl.h>
39 #include "lws_config.h"
42 #include "../lib/libwebsockets.h"
45 * this is specified in the 04 standard, control frames can only have small
46 * payload length styles
48 #define MAX_PING_PAYLOAD 125
49 #define MAX_MIRROR_PAYLOAD 4096
50 #define MAX_PING_CLIENTS 256
51 #define PING_RINGBUFFER_SIZE 256
53 static struct libwebsocket *ping_wsi[MAX_PING_CLIENTS];
54 static unsigned int interval_us = 1000000;
55 static unsigned int size = 64;
57 static const char *address;
58 static unsigned char pingbuf[LWS_SEND_BUFFER_PRE_PADDING + MAX_MIRROR_PAYLOAD +
59 LWS_SEND_BUFFER_POST_PADDING];
60 static char peer_name[128];
61 static unsigned long started;
62 static int screen_width = 80;
63 static int use_mirror;
64 static unsigned int write_options;
66 static unsigned long rtt_min = 100000000;
67 static unsigned long rtt_max;
68 static unsigned long rtt_avg;
69 static unsigned long global_rx_count;
70 static unsigned long global_tx_count;
71 static int clients = 1;
72 static unsigned long interrupted_time;
75 unsigned long issue_timestamp;
80 struct per_session_data__ping {
83 struct ping ringbuffer[PING_RINGBUFFER_SIZE];
87 unsigned long rx_count;
91 * uses the ping pong protocol features to provide an equivalent for the
92 * ping utility for 04+ websockets
105 callback_lws_mirror(struct libwebsocket_context * this,
106 struct libwebsocket *wsi,
107 enum libwebsocket_callback_reasons reason,
108 void *user, void *in, size_t len)
117 struct per_session_data__ping *psd = user;
120 case LWS_CALLBACK_CLOSED:
122 fprintf(stderr, "LWS_CALLBACK_CLOSED on %p\n", (void *)wsi);
124 /* remove closed guy */
126 for (n = 0; n < clients; n++)
127 if (ping_wsi[n] == wsi) {
129 while (n < clients) {
130 ping_wsi[n] = ping_wsi[n + 1];
137 case LWS_CALLBACK_CLIENT_ESTABLISHED:
141 psd->ringbuffer_head = 0;
142 psd->ringbuffer_tail = 0;
145 * start the ball rolling,
146 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
149 libwebsocket_callback_on_writable(this, wsi);
152 case LWS_CALLBACK_CLIENT_RECEIVE:
153 case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
154 gettimeofday(&tv, NULL);
155 iv = (tv.tv_sec * 1000000) + tv.tv_usec;
164 l |= ((uint64_t)*p++) << shift;
168 /* find it in the ringbuffer, look backwards from head */
169 n = psd->ringbuffer_head;
172 if (psd->ringbuffer[n].index == l) {
173 psd->ringbuffer[n].seen++;
178 if (n == psd->ringbuffer_tail) {
184 n = PING_RINGBUFFER_SIZE - 1;
192 fprintf(stderr, "%d bytes from %s: req=%ld "
193 "time=(unknown)\n", (int)len, address,
196 fprintf(stderr, "\b \b");
201 if (psd->ringbuffer[n].seen > 1)
202 fprintf(stderr, "DUP! ");
204 if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min)
205 rtt_min = iv - psd->ringbuffer[n].issue_timestamp;
207 if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max)
208 rtt_max = iv - psd->ringbuffer[n].issue_timestamp;
210 rtt_avg += iv - psd->ringbuffer[n].issue_timestamp;
214 fprintf(stderr, "%d bytes from %s: req=%ld "
215 "time=%lu.%lums\n", (int)len, address, (long)l,
216 (iv - psd->ringbuffer[n].issue_timestamp) / 1000,
217 ((iv - psd->ringbuffer[n].issue_timestamp) / 100) % 10);
219 fprintf(stderr, "\b \b");
222 case LWS_CALLBACK_CLIENT_WRITEABLE:
225 p = &pingbuf[LWS_SEND_BUFFER_PRE_PADDING];
227 /* 64-bit ping index in network byte order */
230 *p++ = psd->ping_index >> shift;
234 while (p - &pingbuf[LWS_SEND_BUFFER_PRE_PADDING] < size)
237 gettimeofday(&tv, NULL);
239 psd->ringbuffer[psd->ringbuffer_head].issue_timestamp =
240 (tv.tv_sec * 1000000) + tv.tv_usec;
241 psd->ringbuffer[psd->ringbuffer_head].index = psd->ping_index++;
242 psd->ringbuffer[psd->ringbuffer_head].seen = 0;
244 if (psd->ringbuffer_head == PING_RINGBUFFER_SIZE - 1)
245 psd->ringbuffer_head = 0;
247 psd->ringbuffer_head++;
249 /* snip any re-used tail so we keep to the ring length */
251 if (psd->ringbuffer_tail == psd->ringbuffer_head) {
252 if (psd->ringbuffer_tail == PING_RINGBUFFER_SIZE - 1)
253 psd->ringbuffer_tail = 0;
255 psd->ringbuffer_tail++;
261 n = libwebsocket_write(wsi,
262 &pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
263 size, write_options | LWS_WRITE_BINARY);
265 n = libwebsocket_write(wsi,
266 &pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
267 size, write_options | LWS_WRITE_PING);
272 lwsl_err("Partial write\n");
277 (psd->ping_index - psd->rx_count) < (screen_width - 1))
278 fprintf(stderr, ".");
289 /* list of supported protocols and callbacks */
291 static struct libwebsocket_protocols protocols[] = {
294 "lws-mirror-protocol",
296 sizeof (struct per_session_data__ping),
299 NULL, NULL, 0/* end of list */
303 static struct option options[] = {
304 { "help", no_argument, NULL, 'h' },
305 { "debug", required_argument, NULL, 'd' },
306 { "port", required_argument, NULL, 'p' },
307 { "ssl", no_argument, NULL, 't' },
308 { "interval", required_argument, NULL, 'i' },
309 { "size", required_argument, NULL, 's' },
310 { "protocol", required_argument, NULL, 'n' },
311 { "flood", no_argument, NULL, 'f' },
312 { "mirror", no_argument, NULL, 'm' },
313 { "replicate", required_argument, NULL, 'r' },
314 { "killmask", no_argument, NULL, 'k' },
315 { "version", required_argument, NULL, 'v' },
321 signal_handler(int sig, siginfo_t *si, void *v)
325 gettimeofday(&tv, NULL);
326 interrupted_time = (tv.tv_sec * 1000000) + tv.tv_usec;
330 int main(int argc, char **argv)
335 struct libwebsocket_context *context;
336 char protocol_name[256];
343 unsigned long oldus = 0;
345 int ietf_version = -1;
346 struct lws_context_creation_info info;
348 memset(&info, 0, sizeof info);
357 n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:d:", options, NULL);
362 lws_set_log_level(atoi(optarg), NULL);
368 use_ssl = 2; /* 2 = allow selfsigned */
374 strncpy(protocol_name, optarg, sizeof protocol_name);
375 protocol_name[(sizeof protocol_name) - 1] = '\0';
376 protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
379 interval_us = 1000000.0 * atof(optarg);
388 clients = atoi(optarg);
389 if (clients > MAX_PING_CLIENTS || clients < 1) {
390 fprintf(stderr, "Max clients supportd = %d\n",
396 write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
399 ietf_version = atoi(optarg);
408 if (size > MAX_PING_PAYLOAD) {
409 fprintf(stderr, "Max ping opcode payload size %d\n",
414 if (size > MAX_MIRROR_PAYLOAD) {
415 fprintf(stderr, "Max mirror payload size %d\n",
422 if (isatty(STDOUT_FILENO))
423 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
425 screen_width = w.ws_col;
428 info.port = CONTEXT_PORT_NO_LISTEN;
429 info.protocols = protocols;
430 #ifndef LWS_NO_EXTENSIONS
431 info.extensions = libwebsocket_get_internal_extensions();
436 context = libwebsocket_create_context(&info);
437 if (context == NULL) {
438 fprintf(stderr, "Creating libwebsocket context failed\n");
442 /* create client websockets using dumb increment protocol */
444 for (n = 0; n < clients; n++) {
445 ping_wsi[n] = libwebsocket_client_connect(context, address,
446 port, use_ssl, "/", address,
447 "origin", protocols[PROTOCOL_LWS_MIRROR].name,
449 if (ping_wsi[n] == NULL) {
450 fprintf(stderr, "client connection %d failed to "
456 libwebsockets_get_peer_addresses(context, ping_wsi[0],
457 libwebsocket_get_socket_fd(ping_wsi[0]),
458 peer_name, sizeof peer_name, ip, sizeof ip);
460 fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n",
461 peer_name, ip, size);
464 /* set the ^C handler */
465 sa.sa_sigaction = signal_handler;
466 sa.sa_flags = SA_SIGINFO;
467 sigemptyset(&sa.sa_mask);
468 sigaction(SIGINT, &sa, NULL);
471 gettimeofday(&tv, NULL);
472 started = (tv.tv_sec * 1000000) + tv.tv_usec;
479 gettimeofday(&tv, NULL);
480 l = (tv.tv_sec * 1000000) + tv.tv_usec;
482 /* servers can hang up on us */
489 if (!interrupted_time) {
490 if ((l - oldus) > interval_us) {
491 for (n = 0; n < clients; n++)
492 libwebsocket_callback_on_writable(
493 context, ping_wsi[n]);
498 /* allow time for in-flight pongs to come */
500 if ((l - interrupted_time) > 250000) {
506 n = libwebsocket_service(context, 0);
508 n = libwebsocket_service(context, 1);
513 fprintf(stderr, "\n--- %s websocket ping statistics "
514 "using %d connections ---\n"
515 "%lu packets transmitted, %lu received, "
516 "%lu%% packet loss, time %ldms\n"
517 "rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n"
518 "payload bandwidth average %0.3f KiBytes/sec\n",
519 peer_name, clients, global_tx_count, global_rx_count,
520 ((global_tx_count - global_rx_count) * 100) / global_tx_count,
521 (l - started) / 1000,
522 ((double)rtt_min) / 1000.0,
523 ((double)rtt_avg / global_rx_count) / 1000.0,
524 ((double)rtt_max) / 1000.0,
525 ((double)global_rx_count * (double)size) /
526 ((double)(l - started) / 1000000.0) / 1024.0);
528 libwebsocket_context_destroy(context);
533 fprintf(stderr, "Usage: libwebsockets-test-ping "
534 "<server address> [--port=<p>] "
535 "[--ssl] [--interval=<float sec>] "
537 "[--protocol=<protocolname>] "
539 "[--replicate=clients>] "
540 "[--version <version>] "
541 "[-d <log bitfield> ]"