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,
31 #include <sys/types.h>
32 #include <sys/socket.h>
35 #include <sys/ioctl.h>
37 #include "../lib/libwebsockets.h"
41 * this is specified in the 04 standard, control frames can only have small
42 * payload length styles
44 #define MAX_PING_PAYLOAD 125
45 #define MAX_MIRROR_PAYLOAD 4096
46 #define MAX_PING_CLIENTS 256
47 #define PING_RINGBUFFER_SIZE 256
49 static unsigned int interval_us = 1000000;
50 static unsigned int size = 64;
52 static const char *address;
53 static unsigned char pingbuf[LWS_SEND_BUFFER_PRE_PADDING + MAX_MIRROR_PAYLOAD +
54 LWS_SEND_BUFFER_POST_PADDING];
55 static char peer_name[128];
56 static unsigned long started;
57 static int screen_width = 80;
58 static int use_mirror;
59 static unsigned int write_options;
61 static unsigned long rtt_min = 100000000;
62 static unsigned long rtt_max;
63 static unsigned long rtt_avg;
64 static unsigned long global_rx_count;
65 static unsigned long global_tx_count;
66 static int clients = 1;
67 static unsigned long interrupted_time;
70 unsigned long issue_timestamp;
75 struct per_session_data__ping {
76 unsigned long ping_index;
78 struct ping ringbuffer[PING_RINGBUFFER_SIZE];
82 unsigned long rx_count;
86 * uses the ping pong protocol features to provide an equivalent for the
87 * ping utility for 04+ websockets
100 callback_lws_mirror(struct libwebsocket *wsi,
101 enum libwebsocket_callback_reasons reason,
102 void *user, void *in, size_t len)
111 struct per_session_data__ping *psd = user;
114 case LWS_CALLBACK_CLIENT_ESTABLISHED:
118 psd->ringbuffer_head = 0;
119 psd->ringbuffer_tail = 0;
122 * start the ball rolling,
123 * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
126 libwebsocket_callback_on_writable(wsi);
129 case LWS_CALLBACK_CLIENT_RECEIVE:
130 case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
131 gettimeofday(&tv, NULL);
132 iv = (tv.tv_sec * 1000000) + tv.tv_usec;
141 l |= (*p++) << shift;
145 /* find it in the ringbuffer, look backwards from head */
146 n = psd->ringbuffer_head;
149 if (psd->ringbuffer[n].index == l) {
150 psd->ringbuffer[n].seen++;
155 if (n == psd->ringbuffer_tail) {
161 n = PING_RINGBUFFER_SIZE - 1;
169 fprintf(stderr, "%d bytes from %s: req=%ld "
170 "time=(unknown)\n", (int)len, address, l);
172 fprintf(stderr, "\b \b");
177 if (psd->ringbuffer[n].seen > 1)
178 fprintf(stderr, "DUP! ");
180 if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min)
181 rtt_min = iv - psd->ringbuffer[n].issue_timestamp;
183 if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max)
184 rtt_max = iv - psd->ringbuffer[n].issue_timestamp;
186 rtt_avg += iv - psd->ringbuffer[n].issue_timestamp;
190 fprintf(stderr, "%d bytes from %s: req=%ld "
191 "time=%lu.%lums\n", (int)len, address, l,
192 (iv - psd->ringbuffer[n].issue_timestamp) / 1000,
193 ((iv - psd->ringbuffer[n].issue_timestamp) / 100) % 10);
195 fprintf(stderr, "\b \b");
198 case LWS_CALLBACK_CLIENT_WRITEABLE:
201 p = &pingbuf[LWS_SEND_BUFFER_PRE_PADDING];
203 /* 64-bit ping index in network byte order */
206 *p++ = psd->ping_index >> shift;
210 gettimeofday(&tv, NULL);
212 psd->ringbuffer[psd->ringbuffer_head].issue_timestamp =
213 (tv.tv_sec * 1000000) + tv.tv_usec;
214 psd->ringbuffer[psd->ringbuffer_head].index = psd->ping_index++;
215 psd->ringbuffer[psd->ringbuffer_head].seen = 0;
217 if (psd->ringbuffer_head == PING_RINGBUFFER_SIZE - 1)
218 psd->ringbuffer_head = 0;
220 psd->ringbuffer_head++;
222 /* snip any re-used tail so we keep to the ring length */
224 if (psd->ringbuffer_tail == psd->ringbuffer_head) {
225 if (psd->ringbuffer_tail == PING_RINGBUFFER_SIZE - 1)
226 psd->ringbuffer_tail = 0;
228 psd->ringbuffer_tail++;
234 libwebsocket_write(wsi,
235 &pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
236 size, write_options | LWS_WRITE_BINARY);
238 libwebsocket_write(wsi,
239 &pingbuf[LWS_SEND_BUFFER_PRE_PADDING],
240 size, write_options | LWS_WRITE_PING);
243 (psd->ping_index - psd->rx_count) < (screen_width - 1))
244 fprintf(stderr, ".");
255 /* list of supported protocols and callbacks */
257 static struct libwebsocket_protocols protocols[] = {
259 [PROTOCOL_LWS_MIRROR] = {
260 .name = "lws-mirror-protocol",
261 .callback = callback_lws_mirror,
262 .per_session_data_size = sizeof (struct per_session_data__ping),
264 [DEMO_PROTOCOL_COUNT] = { /* end of list */
269 static struct option options[] = {
270 { "help", no_argument, NULL, 'h' },
271 { "port", required_argument, NULL, 'p' },
272 { "ssl", no_argument, NULL, 't' },
273 { "interval", required_argument, NULL, 'i' },
274 { "size", required_argument, NULL, 's' },
275 { "protocol", required_argument, NULL, 'n' },
276 { "flood", no_argument, NULL, 'f' },
277 { "mirror", no_argument, NULL, 'm' },
278 { "replicate", required_argument, NULL, 'r' },
279 { "killmask", no_argument, NULL, 'k' },
280 { "version", required_argument, NULL, 'v' },
286 signal_handler(int sig, siginfo_t *si, void *v)
290 gettimeofday(&tv, NULL);
291 interrupted_time = (tv.tv_sec * 1000000) + tv.tv_usec;
295 int main(int argc, char **argv)
300 struct libwebsocket_context *context;
301 struct libwebsocket *wsi[MAX_PING_CLIENTS];
302 char protocol_name[256];
307 unsigned long oldus = 0;
309 int ietf_version = -1;
318 n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:", options, NULL);
326 use_ssl = 2; /* 2 = allow selfsigned */
332 strncpy(protocol_name, optarg, sizeof protocol_name);
333 protocol_name[(sizeof protocol_name) - 1] = '\0';
334 protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
337 interval_us = 1000000.0 * atof(optarg);
346 clients = atoi(optarg);
347 if (clients > MAX_PING_CLIENTS || clients < 1) {
348 fprintf(stderr, "Max clients supportd = %d\n",
354 write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
357 ietf_version = atoi(optarg);
366 if (size > MAX_PING_PAYLOAD) {
367 fprintf(stderr, "Max ping opcode payload size %d\n",
372 if (size > MAX_MIRROR_PAYLOAD) {
373 fprintf(stderr, "Max mirror payload size %d\n",
380 if (isatty(STDOUT_FILENO))
381 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
383 screen_width = w.ws_col;
385 context = libwebsocket_create_context(CONTEXT_PORT_NO_LISTEN,
386 protocols, NULL, NULL, -1, -1, 0);
387 if (context == NULL) {
388 fprintf(stderr, "Creating libwebsocket context failed\n");
392 /* create client websockets using dumb increment protocol */
394 for (n = 0; n < clients; n++) {
395 wsi[n] = libwebsocket_client_connect(context, address, port,
396 use_ssl, "/", address,
397 "origin", protocols[PROTOCOL_LWS_MIRROR].name,
399 if (wsi[n] == NULL) {
400 fprintf(stderr, "client connnection %d failed to "
406 libwebsockets_get_peer_addresses(libwebsocket_get_socket_fd(wsi[0]),
407 peer_name, sizeof peer_name, ip, sizeof ip);
409 fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n",
410 peer_name, ip, size);
412 /* set the ^C handler */
414 sa.sa_sigaction = signal_handler;
415 sa.sa_flags = SA_SIGINFO;
416 sigemptyset(&sa.sa_mask);
417 sigaction(SIGINT, &sa, NULL);
419 gettimeofday(&tv, NULL);
420 started = (tv.tv_sec * 1000000) + tv.tv_usec;
427 gettimeofday(&tv, NULL);
428 l = (tv.tv_sec * 1000000) + tv.tv_usec;
431 if (!interrupted_time) {
432 if ((l - oldus) > interval_us) {
433 for (n = 0; n < clients; n++)
434 libwebsocket_callback_on_writable(
440 /* allow time for in-flight pongs to come */
442 if ((l - interrupted_time) > 250000) {
448 n = libwebsocket_service(context, 0);
450 n = libwebsocket_service(context, 1);
455 fprintf(stderr, "\n--- %s websocket ping statistics "
456 "using %d connections ---\n"
457 "%lu packets transmitted, %lu received, "
458 "%lu%% packet loss, time %ldms\n"
459 "rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n"
460 "payload bandwidth average %0.3f KiBytes/sec\n",
461 peer_name, clients, global_tx_count, global_rx_count,
462 ((global_tx_count - global_rx_count) * 100) / global_tx_count,
463 (l - started) / 1000,
464 ((double)rtt_min) / 1000.0,
465 ((double)rtt_avg / global_rx_count) / 1000.0,
466 ((double)rtt_max) / 1000.0,
467 ((double)global_rx_count * (double)size) /
468 ((double)(l - started) / 1000000.0) / 1024.0);
470 libwebsocket_context_destroy(context);
475 fprintf(stderr, "Usage: libwebsockets-test-ping "
476 "<server address> [--port=<p>] "
477 "[--ssl] [--interval=<float sec>] "
479 "[--protocol=<protocolname>] "
481 "[--replicate=clients>]"
482 "[--version <version>]"