test-ping.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /*
  2. * libwebsockets-test-ping - libwebsockets test floodping
  3. *
  4. * Copyright (C) 2011-2016 Andy Green <andy@warmcat.com>
  5. *
  6. * This file is made available under the Creative Commons CC0 1.0
  7. * Universal Public Domain Dedication.
  8. *
  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.
  15. *
  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
  18. * Public Domain.
  19. */
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <getopt.h>
  23. #include <string.h>
  24. #include <signal.h>
  25. #include <sys/types.h>
  26. #include "../lib/libwebsockets.h"
  27. #ifndef _WIN32
  28. #include <netdb.h>
  29. #include <sys/socket.h>
  30. #include <sys/time.h>
  31. #include <sys/ioctl.h>
  32. #include <poll.h>
  33. #include <unistd.h>
  34. #else
  35. #include "gettimeofday.h"
  36. #endif
  37. #ifdef __ANDROID__
  38. #include <termiosh>
  39. #endif
  40. #ifdef __sun
  41. #include <sys/termios.h>
  42. #endif
  43. /*
  44. * this is specified in the 04 standard, control frames can only have small
  45. * payload length styles
  46. */
  47. #define MAX_PING_PAYLOAD 125
  48. #define MAX_MIRROR_PAYLOAD 4096
  49. #define MAX_PING_CLIENTS 256
  50. #define PING_RINGBUFFER_SIZE 256
  51. static struct lws *ping_wsi[MAX_PING_CLIENTS];
  52. static unsigned int interval_us = 1000000;
  53. static unsigned int size = 64;
  54. static int flood;
  55. static const char *address;
  56. static unsigned char pingbuf[LWS_PRE + MAX_MIRROR_PAYLOAD];
  57. static char peer_name[128];
  58. static unsigned long started;
  59. static int screen_width = 80;
  60. static int use_mirror;
  61. static unsigned int write_options;
  62. static unsigned long rtt_min = 100000000;
  63. static unsigned long rtt_max;
  64. static unsigned long rtt_avg;
  65. static unsigned long global_rx_count;
  66. static unsigned long global_tx_count;
  67. static int clients = 1;
  68. static unsigned long interrupted_time;
  69. struct ping {
  70. unsigned long issue_timestamp;
  71. unsigned long index;
  72. unsigned int seen;
  73. };
  74. struct per_session_data__ping {
  75. unsigned long long ping_index;
  76. struct ping ringbuffer[PING_RINGBUFFER_SIZE];
  77. int ringbuffer_head;
  78. int ringbuffer_tail;
  79. unsigned long rx_count;
  80. };
  81. /*
  82. * uses the ping pong protocol features to provide an equivalent for the
  83. * ping utility for 04+ websockets
  84. */
  85. enum demo_protocols {
  86. PROTOCOL_LWS_MIRROR,
  87. /* always last */
  88. DEMO_PROTOCOL_COUNT
  89. };
  90. static int
  91. callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
  92. void *user, void *in, size_t len)
  93. {
  94. struct per_session_data__ping *psd = user;
  95. struct timeval tv;
  96. unsigned char *p;
  97. unsigned long iv;
  98. int match = 0;
  99. unsigned long long l;
  100. int shift;
  101. int n;
  102. switch (reason) {
  103. case LWS_CALLBACK_CLOSED:
  104. fprintf(stderr, "LWS_CALLBACK_CLOSED on %p\n", (void *)wsi);
  105. /* remove closed guy */
  106. for (n = 0; n < clients; n++)
  107. if (ping_wsi[n] == wsi) {
  108. clients--;
  109. while (n < clients) {
  110. ping_wsi[n] = ping_wsi[n + 1];
  111. n++;
  112. }
  113. }
  114. break;
  115. case LWS_CALLBACK_CLIENT_ESTABLISHED:
  116. psd->rx_count = 0;
  117. psd->ping_index = 1;
  118. psd->ringbuffer_head = 0;
  119. psd->ringbuffer_tail = 0;
  120. /*
  121. * start the ball rolling,
  122. * LWS_CALLBACK_CLIENT_WRITEABLE will come next service
  123. */
  124. lws_callback_on_writable(wsi);
  125. break;
  126. case LWS_CALLBACK_CLIENT_RECEIVE:
  127. case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
  128. gettimeofday(&tv, NULL);
  129. iv = (tv.tv_sec * 1000000) + tv.tv_usec;
  130. psd->rx_count++;
  131. shift = 56;
  132. p = in;
  133. l = 0;
  134. while (shift >= 0) {
  135. l |= ((unsigned long long)*p++) << shift;
  136. shift -= 8;
  137. }
  138. /* find it in the ringbuffer, look backwards from head */
  139. n = psd->ringbuffer_head;
  140. while (!match) {
  141. if (psd->ringbuffer[n].index == l) {
  142. psd->ringbuffer[n].seen++;
  143. match = 1;
  144. continue;
  145. }
  146. if (n == psd->ringbuffer_tail) {
  147. match = -1;
  148. continue;
  149. }
  150. if (n == 0)
  151. n = PING_RINGBUFFER_SIZE - 1;
  152. else
  153. n--;
  154. }
  155. if (match < 1) {
  156. if (!flood)
  157. fprintf(stderr, "%d bytes from %s: req=%ld "
  158. "time=(unknown)\n", (int)len, address,
  159. (long)l);
  160. else
  161. fprintf(stderr, "\b \b");
  162. break;
  163. }
  164. if (psd->ringbuffer[n].seen > 1)
  165. fprintf(stderr, "DUP! ");
  166. if ((iv - psd->ringbuffer[n].issue_timestamp) < rtt_min)
  167. rtt_min = iv - psd->ringbuffer[n].issue_timestamp;
  168. if ((iv - psd->ringbuffer[n].issue_timestamp) > rtt_max)
  169. rtt_max = iv - psd->ringbuffer[n].issue_timestamp;
  170. rtt_avg += iv - psd->ringbuffer[n].issue_timestamp;
  171. global_rx_count++;
  172. if (!flood)
  173. fprintf(stderr, "%d bytes from %s: req=%ld "
  174. "time=%lu.%lums\n", (int)len, address, (long)l,
  175. (iv - psd->ringbuffer[n].issue_timestamp) / 1000,
  176. ((iv - psd->ringbuffer[n].issue_timestamp) / 100) % 10);
  177. else
  178. fprintf(stderr, "\b \b");
  179. break;
  180. case LWS_CALLBACK_CLIENT_WRITEABLE:
  181. shift = 56;
  182. p = &pingbuf[LWS_PRE];
  183. /* 64-bit ping index in network byte order */
  184. while (shift >= 0) {
  185. *p++ = (unsigned char)(psd->ping_index >> shift);
  186. shift -= 8;
  187. }
  188. while ((unsigned int)(p - &pingbuf[LWS_PRE]) < size)
  189. *p++ = 0;
  190. gettimeofday(&tv, NULL);
  191. psd->ringbuffer[psd->ringbuffer_head].issue_timestamp =
  192. (tv.tv_sec * 1000000) + tv.tv_usec;
  193. psd->ringbuffer[psd->ringbuffer_head].index = (unsigned long)psd->ping_index++;
  194. psd->ringbuffer[psd->ringbuffer_head].seen = 0;
  195. if (psd->ringbuffer_head == PING_RINGBUFFER_SIZE - 1)
  196. psd->ringbuffer_head = 0;
  197. else
  198. psd->ringbuffer_head++;
  199. /* snip any re-used tail so we keep to the ring length */
  200. if (psd->ringbuffer_tail == psd->ringbuffer_head) {
  201. if (psd->ringbuffer_tail == PING_RINGBUFFER_SIZE - 1)
  202. psd->ringbuffer_tail = 0;
  203. else
  204. psd->ringbuffer_tail++;
  205. }
  206. global_tx_count++;
  207. if (use_mirror)
  208. n = lws_write(wsi,
  209. &pingbuf[LWS_PRE],
  210. size, write_options | LWS_WRITE_BINARY);
  211. else
  212. n = lws_write(wsi,
  213. &pingbuf[LWS_PRE],
  214. size, write_options | LWS_WRITE_PING);
  215. if (n < 0)
  216. return -1;
  217. if (n < (int)size) {
  218. lwsl_err("Partial write\n");
  219. return -1;
  220. }
  221. if (flood &&
  222. (psd->ping_index - psd->rx_count) < (screen_width - 1))
  223. fprintf(stderr, ".");
  224. break;
  225. default:
  226. break;
  227. }
  228. return 0;
  229. }
  230. /* list of supported protocols and callbacks */
  231. static struct lws_protocols protocols[] = {
  232. {
  233. "lws-mirror-protocol",
  234. callback_lws_mirror,
  235. sizeof (struct per_session_data__ping),
  236. },
  237. {
  238. NULL, NULL, 0/* end of list */
  239. }
  240. };
  241. static const struct lws_extension exts[] = {
  242. {
  243. "permessage-deflate",
  244. lws_extension_callback_pm_deflate,
  245. "permessage-deflate; client_no_context_takeover; client_max_window_bits"
  246. },
  247. {
  248. "deflate-frame",
  249. lws_extension_callback_pm_deflate,
  250. "deflate_frame"
  251. },
  252. { NULL, NULL, NULL /* terminator */ }
  253. };
  254. static struct option options[] = {
  255. { "help", no_argument, NULL, 'h' },
  256. { "debug", required_argument, NULL, 'd' },
  257. { "port", required_argument, NULL, 'p' },
  258. { "ssl", no_argument, NULL, 't' },
  259. { "interval", required_argument, NULL, 'i' },
  260. { "size", required_argument, NULL, 's' },
  261. { "protocol", required_argument, NULL, 'n' },
  262. { "flood", no_argument, NULL, 'f' },
  263. { "mirror", no_argument, NULL, 'm' },
  264. { "replicate", required_argument, NULL, 'r' },
  265. { "killmask", no_argument, NULL, 'k' },
  266. { "version", required_argument, NULL, 'v' },
  267. { NULL, 0, 0, 0 }
  268. };
  269. #ifndef _WIN32
  270. static void
  271. signal_handler(int sig, siginfo_t *si, void *v)
  272. {
  273. struct timeval tv;
  274. gettimeofday(&tv, NULL);
  275. interrupted_time = (tv.tv_sec * 1000000) + tv.tv_usec;
  276. }
  277. #endif
  278. int main(int argc, char **argv)
  279. {
  280. int n = 0;
  281. int port = 7681;
  282. int use_ssl = 0;
  283. struct lws_context *context;
  284. char protocol_name[256], ads_port[300];
  285. char ip[30];
  286. #ifndef _WIN32
  287. struct sigaction sa;
  288. struct winsize w;
  289. #endif
  290. struct timeval tv;
  291. unsigned long oldus = 0;
  292. unsigned long l;
  293. int ietf_version = -1;
  294. struct lws_context_creation_info info;
  295. struct lws_client_connect_info i;
  296. memset(&info, 0, sizeof info);
  297. if (argc < 2)
  298. goto usage;
  299. while (n >= 0) {
  300. n = getopt_long(argc, argv, "v:kr:hmfts:n:i:p:d:", options, NULL);
  301. if (n < 0)
  302. continue;
  303. switch (n) {
  304. case 'd':
  305. lws_set_log_level(atoi(optarg), NULL);
  306. break;
  307. case 'm':
  308. use_mirror = 1;
  309. break;
  310. case 't':
  311. use_ssl = 2; /* 2 = allow selfsigned */
  312. break;
  313. case 'p':
  314. port = atoi(optarg);
  315. break;
  316. case 'n':
  317. strncpy(protocol_name, optarg, sizeof protocol_name);
  318. protocol_name[(sizeof protocol_name) - 1] = '\0';
  319. protocols[PROTOCOL_LWS_MIRROR].name = protocol_name;
  320. break;
  321. case 'i':
  322. interval_us = (unsigned int)(1000000.0 * atof(optarg));
  323. break;
  324. case 's':
  325. size = atoi(optarg);
  326. break;
  327. case 'f':
  328. flood = 1;
  329. break;
  330. case 'r':
  331. clients = atoi(optarg);
  332. if (clients > MAX_PING_CLIENTS || clients < 1) {
  333. fprintf(stderr, "Max clients supported = %d\n",
  334. MAX_PING_CLIENTS);
  335. return 1;
  336. }
  337. break;
  338. case 'k':
  339. write_options = LWS_WRITE_CLIENT_IGNORE_XOR_MASK;
  340. break;
  341. case 'v':
  342. ietf_version = atoi(optarg);
  343. break;
  344. case 'h':
  345. goto usage;
  346. }
  347. }
  348. if (!use_mirror) {
  349. if (size > MAX_PING_PAYLOAD) {
  350. fprintf(stderr, "Max ping opcode payload size %d\n",
  351. MAX_PING_PAYLOAD);
  352. return 1;
  353. }
  354. } else {
  355. if (size > MAX_MIRROR_PAYLOAD) {
  356. fprintf(stderr, "Max mirror payload size %d\n",
  357. MAX_MIRROR_PAYLOAD);
  358. return 1;
  359. }
  360. }
  361. #ifndef _WIN32
  362. if (isatty(STDOUT_FILENO))
  363. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
  364. if (w.ws_col > 0)
  365. screen_width = w.ws_col;
  366. #endif
  367. info.port = CONTEXT_PORT_NO_LISTEN;
  368. info.protocols = protocols;
  369. info.extensions = exts;
  370. info.gid = -1;
  371. info.uid = -1;
  372. if (use_ssl)
  373. info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
  374. context = lws_create_context(&info);
  375. if (context == NULL) {
  376. fprintf(stderr, "Creating libwebsocket context failed\n");
  377. return 1;
  378. }
  379. /* create client websockets using dumb increment protocol */
  380. address = argv[optind];
  381. lws_snprintf(ads_port, sizeof(ads_port), "%s:%u",
  382. address, port & 65535);
  383. lwsl_notice("Connecting to %s...\n", ads_port);
  384. memset(&i, 0, sizeof(i));
  385. i.context = context;
  386. i.address = address;
  387. i.port = port;
  388. i.ssl_connection = use_ssl;
  389. i.path = "/";
  390. i.host = ads_port;
  391. i.origin = ads_port;
  392. i.protocol = protocols[PROTOCOL_LWS_MIRROR].name;
  393. i.ietf_version_or_minus_one = ietf_version;
  394. for (n = 0; n < clients; n++) {
  395. ping_wsi[n] = lws_client_connect_via_info(&i);
  396. if (ping_wsi[n] == NULL) {
  397. lwsl_err("client %d failed to connect\n", n);
  398. return 1;
  399. }
  400. }
  401. lws_get_peer_addresses(ping_wsi[0], lws_get_socket_fd(ping_wsi[0]),
  402. peer_name, sizeof peer_name, ip, sizeof ip);
  403. lwsl_notice("libwebsockets test server ping - license LGPL2.1+SLE\n");
  404. lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");
  405. fprintf(stderr, "Websocket PING %s (%s) %d bytes of data.\n",
  406. peer_name, ip, size);
  407. #ifndef _WIN32
  408. /* set the ^C handler */
  409. sa.sa_sigaction = signal_handler;
  410. sa.sa_flags = SA_SIGINFO;
  411. sigemptyset(&sa.sa_mask);
  412. sigaction(SIGINT, &sa, NULL);
  413. #endif
  414. gettimeofday(&tv, NULL);
  415. started = (tv.tv_sec * 1000000) + tv.tv_usec;
  416. /* service loop */
  417. n = 0;
  418. while (n >= 0) {
  419. gettimeofday(&tv, NULL);
  420. l = (tv.tv_sec * 1000000) + tv.tv_usec;
  421. /* servers can hang up on us */
  422. if (clients == 0) {
  423. n = -1;
  424. continue;
  425. }
  426. if (!interrupted_time) {
  427. if ((l - oldus) > interval_us) {
  428. for (n = 0; n < clients; n++)
  429. lws_callback_on_writable(ping_wsi[n]);
  430. oldus = l;
  431. }
  432. } else
  433. /* allow time for in-flight pongs to come */
  434. if ((l - interrupted_time) > 250000) {
  435. n = -1;
  436. continue;
  437. }
  438. if (!interval_us)
  439. n = lws_service(context, 0);
  440. else
  441. n = lws_service(context, 1);
  442. }
  443. /* stats */
  444. fprintf(stderr, "\n--- %s websocket ping statistics "
  445. "using %d connections ---\n"
  446. "%lu packets transmitted, %lu received, "
  447. "%lu%% packet loss, time %ldms\n"
  448. "rtt min/avg/max = %0.3f/%0.3f/%0.3f ms\n"
  449. "payload bandwidth average %0.3f KiBytes/sec\n",
  450. peer_name, clients, global_tx_count, global_rx_count,
  451. ((global_tx_count - global_rx_count) * 100) / global_tx_count,
  452. (l - started) / 1000,
  453. ((double)rtt_min) / 1000.0,
  454. ((double)rtt_avg / global_rx_count) / 1000.0,
  455. ((double)rtt_max) / 1000.0,
  456. ((double)global_rx_count * (double)size) /
  457. ((double)(l - started) / 1000000.0) / 1024.0);
  458. lws_context_destroy(context);
  459. return 0;
  460. usage:
  461. fprintf(stderr, "Usage: libwebsockets-test-ping "
  462. "<server address> [--port=<p>] "
  463. "[--ssl] [--interval=<float sec>] "
  464. "[--size=<bytes>] "
  465. "[--protocol=<protocolname>] "
  466. "[--mirror] "
  467. "[--replicate=clients>] "
  468. "[--version <version>] "
  469. "[-d <log bitfield> ]"
  470. "\n");
  471. return 1;
  472. }