mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
minimal examples: ws client demonstrating retry and backoff
Add a simple example to demonstrate the current best approach for connection retry and backoff
This commit is contained in:
parent
414516cad4
commit
0e1b4cf389
5 changed files with 411 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
|||
|name|demonstrates|
|
||||
---|---
|
||||
minimal-ws-client|Simple client that connects to libwebsockets.org dumb increment protocol and demonstrates retry and backoff
|
||||
minimal-ws-client-echo|Simple client that connects to a ws server and echos anything the server sends
|
||||
minimal-ws-client-ping|Ws ping test client
|
||||
minimal-ws-client-pmd-bulk|Client that sends bulk multifragment data to the minimal-ws-server-pmd-bulk example
|
||||
|
|
80
minimal-examples/ws-client/minimal-ws-client/CMakeLists.txt
Normal file
80
minimal-examples/ws-client/minimal-ws-client/CMakeLists.txt
Normal file
|
@ -0,0 +1,80 @@
|
|||
project(lws-minimal-ws-client-ping)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
include(CheckIncludeFile)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(SAMP lws-minimal-ws-client)
|
||||
set(SRCS minimal-ws-client.c)
|
||||
|
||||
# If we are being built as part of lws, confirm current build config supports
|
||||
# reqconfig, else skip building ourselves.
|
||||
#
|
||||
# If we are being built externally, confirm installed lws was configured to
|
||||
# support reqconfig, else error out with a helpful message about the problem.
|
||||
#
|
||||
MACRO(require_lws_config reqconfig _val result)
|
||||
|
||||
if (DEFINED ${reqconfig})
|
||||
if (${reqconfig})
|
||||
set (rq 1)
|
||||
else()
|
||||
set (rq 0)
|
||||
endif()
|
||||
else()
|
||||
set(rq 0)
|
||||
endif()
|
||||
|
||||
if (${_val} EQUAL ${rq})
|
||||
set(SAME 1)
|
||||
else()
|
||||
set(SAME 0)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
|
||||
if (${_val})
|
||||
message("${SAMP}: skipping as lws being built without ${reqconfig}")
|
||||
else()
|
||||
message("${SAMP}: skipping as lws built with ${reqconfig}")
|
||||
endif()
|
||||
set(${result} 0)
|
||||
else()
|
||||
if (LWS_WITH_MINIMAL_EXAMPLES)
|
||||
set(MET ${SAME})
|
||||
else()
|
||||
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
|
||||
if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
|
||||
set(HAS_${reqconfig} 0)
|
||||
else()
|
||||
set(HAS_${reqconfig} 1)
|
||||
endif()
|
||||
if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
|
||||
set(MET 1)
|
||||
else()
|
||||
set(MET 0)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT MET)
|
||||
if (${_val})
|
||||
message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
|
||||
else()
|
||||
message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
ENDMACRO()
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_ROLE_WS 1 requirements)
|
||||
require_lws_config(LWS_WITH_CLIENT 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared)
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets)
|
||||
endif()
|
||||
endif()
|
58
minimal-examples/ws-client/minimal-ws-client/README.md
Normal file
58
minimal-examples/ws-client/minimal-ws-client/README.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# lws minimal ws client
|
||||
|
||||
This connects to libwebsockets.org using the dumb-increment-protocol.
|
||||
|
||||
It demonstrates how to use the connection retry and backoff stuff in lws.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## Commandline Options
|
||||
|
||||
Option|Meaning
|
||||
---|---
|
||||
-d|Set logging verbosity
|
||||
-s|Use a specific server instead of libwebsockets.org, eg `--server localhost`. Implies LCCSCF_ALLOW_SELFSIGNED
|
||||
-p|Use a specific port instead of 443, eg `--port 7681`
|
||||
-j|Allow selfsigned tls cert
|
||||
-k|Allow insecure certs
|
||||
-m|Skip server hostname check
|
||||
-e|Allow expired certs
|
||||
--protocol|Use a specific ws subprotocol rather than dumb-increment-protocol, eg, `--protocol myprotocol`
|
||||
|
||||
|
||||
## usage
|
||||
|
||||
Just run it, it will connect to libwebsockets.org and spew incrementing numbers
|
||||
sent by the server at 20Hz
|
||||
|
||||
```
|
||||
$ ./lws-minimal-ws-client
|
||||
[2020/01/22 05:38:47:3409] U: LWS minimal ws client
|
||||
[2020/01/22 05:38:47:4456] N: Loading client CA for verification ./libwebsockets.org.cer
|
||||
[2020/01/22 05:38:48:1649] U: callback_minimal: established
|
||||
[2020/01/22 05:38:48:1739] N:
|
||||
[2020/01/22 05:38:48:1763] N: 0000: 30 0
|
||||
[2020/01/22 05:38:48:1765] N:
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
To test against the lws test server instead of libwebsockets.org, run the test
|
||||
server as
|
||||
|
||||
```
|
||||
$ libwebsockets-test-server -s
|
||||
```
|
||||
|
||||
and run this test app with
|
||||
|
||||
```
|
||||
$ ./lws-minimal-ws-client -s localhost -p 7681 -j
|
||||
```
|
||||
|
||||
You can kill and restart the server to confirm the client connection is re-
|
||||
established if done within the backoff period.
|
|
@ -0,0 +1,58 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFWjCCBEKgAwIBAgISA9x0/oj5PLdW46hsmR82/7ytMA0GCSqGSIb3DQEBCwUA
|
||||
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
|
||||
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xOTA5MDcwNzA5NDBaFw0x
|
||||
OTEyMDYwNzA5NDBaMBwxGjAYBgNVBAMTEWxpYndlYnNvY2tldHMub3JnMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPinIkleLmvEcA/YuBss6ASXVi7g
|
||||
yr6Sss7cB3vTy7Fp8OB2c1N25prHZxVpORAUo0UreiaY2Ws4NFvDaYp08ZffevuC
|
||||
UhThsEJlbkD0uvt7dPapJt9PNJtlxjNFWyvHEy6PijzIaMYDROiStcCJQn7kAew/
|
||||
Za2+5kNVgKqT+7OXukJEFdSdVZI6QC/npeQlkIrFSq1WVthCGBNJehxxES0hSWzk
|
||||
0gNVKlkD3/SbkupsfUpe73XiawMtrtsSE7cdnul7VZmiP8I/3sJr1+4/3xZ+DEYg
|
||||
mVB82B0vd08VJYzU7Nf0pz0PWusAmzRoRn81IXkOfBg9ohlSSEoZhHYS7QIDAQAB
|
||||
o4ICZjCCAmIwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
|
||||
BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRmKKyGjufWgp7pR2x0tWxG
|
||||
D9G+WTAfBgNVHSMEGDAWgBSoSmpjBH3duubRObemRWXv86jsoTBvBggrBgEFBQcB
|
||||
AQRjMGEwLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3NwLmludC14My5sZXRzZW5jcnlw
|
||||
dC5vcmcwLwYIKwYBBQUHMAKGI2h0dHA6Ly9jZXJ0LmludC14My5sZXRzZW5jcnlw
|
||||
dC5vcmcvMBwGA1UdEQQVMBOCEWxpYndlYnNvY2tldHMub3JnMEwGA1UdIARFMEMw
|
||||
CAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly9j
|
||||
cHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHWeQIEAgSB9QSB8gDwAHcAdH7a
|
||||
gzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYAAAFtCsWIfgAABAMASDBGAiEA
|
||||
0H55VqSKV3otHK7uHNbcR0QwoUYtCmeObhsqxzCnmDwCIQD3mtuSKrxTD3oA+Yde
|
||||
nmTgWfFyS4TNgLNEPCJYo2s75gB1ACk8UZZUyDlluqpQ/FgH1Ldvv1h6KXLcpMMM
|
||||
9OVFR/R4AAABbQrFil4AAAQDAEYwRAIgNSpvz/1JA2aP6fh6ujGNuYfrAvWjlxXo
|
||||
CJtVGe4XaDYCIGmK1/9tl1uQbVD46P5NswnULq06KQmuOrlI3HO4r86HMA0GCSqG
|
||||
SIb3DQEBCwUAA4IBAQBiAlV7wkCsWE99VmZHBmcbZChWyWUHG3LM1hnaQRQjTSYk
|
||||
CIlauCpWzlUd6weuvra85KqBbCYo+1hxbwITI796uAdgtHmBE8nj0VltHwKeSq2s
|
||||
KKiGXBRT7Z7t0VHYSLOlGOVn1auuQFaWBArc0cQ/m1ZsoHvOiHTlKQvVsA4HnIxA
|
||||
CjGY9OOQoh0c36ecbJZ44XKnU9J/OXtDx00aW6QodaZmgMp/OOCghFQUvufkgTUL
|
||||
LZid873/8dJVWjAaj1VdadO1nSbdAfBbeWXy93+vg1aAoig80RoscrzYCaNlwmR7
|
||||
EO5zWxL3l+xUZogQSJuICgUgNzVB3wjn8HeHGsqt
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
|
||||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
|
||||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
|
||||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
|
||||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
|
||||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
|
||||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
|
||||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
|
||||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
|
||||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
|
||||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
|
||||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
|
||||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
|
||||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
|
||||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
|
||||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
|
||||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
|
||||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
|
||||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
|
||||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
|
||||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
|
||||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
|
||||
-----END CERTIFICATE-----
|
214
minimal-examples/ws-client/minimal-ws-client/minimal-ws-client.c
Normal file
214
minimal-examples/ws-client/minimal-ws-client/minimal-ws-client.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* lws-minimal-ws-client
|
||||
*
|
||||
* Written in 2010-2020 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This demonstrates a ws client that connects by default to libwebsockets.org
|
||||
* dumb increment ws server.
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* This represents your object that "contains" the client connection and has
|
||||
* the client connection bound to it
|
||||
*/
|
||||
|
||||
static struct my_conn {
|
||||
lws_sorted_usec_list_t sul; /* schedule connection retry */
|
||||
struct lws *wsi; /* related wsi if any */
|
||||
uint16_t retry_count; /* count of consequetive retries */
|
||||
} mco;
|
||||
|
||||
static struct lws_context *context;
|
||||
static int interrupted, port = 443, ssl_connection = LCCSCF_USE_SSL;
|
||||
static const char *server_address = "libwebsockets.org",
|
||||
*pro = "dumb-increment-protocol";
|
||||
|
||||
/*
|
||||
* The retry and backoff policy we want to use for our client connections
|
||||
*/
|
||||
|
||||
static const uint32_t backoff_ms[] = { 1000, 2000, 3000, 4000, 5000 };
|
||||
|
||||
static const lws_retry_bo_t retry = {
|
||||
.retry_ms_table = backoff_ms,
|
||||
.retry_ms_table_count = LWS_ARRAY_SIZE(backoff_ms),
|
||||
.conceal_count = LWS_ARRAY_SIZE(backoff_ms),
|
||||
|
||||
.secs_since_valid_ping = 3, /* force PINGs after secs idle */
|
||||
.secs_since_valid_hangup = 10, /* hangup after secs idle */
|
||||
|
||||
.jitter_percent = 20,
|
||||
};
|
||||
|
||||
/*
|
||||
* Scheduled sul callback that starts the connection attempt
|
||||
*/
|
||||
|
||||
static void
|
||||
connect_client(lws_sorted_usec_list_t *sul)
|
||||
{
|
||||
struct my_conn *mco = lws_container_of(sul, struct my_conn, sul);
|
||||
struct lws_client_connect_info i;
|
||||
|
||||
memset(&i, 0, sizeof(i));
|
||||
|
||||
i.context = context;
|
||||
i.port = port;
|
||||
i.address = server_address;
|
||||
i.path = "/";
|
||||
i.host = i.address;
|
||||
i.origin = i.address;
|
||||
i.ssl_connection = ssl_connection;
|
||||
i.protocol = pro;
|
||||
i.local_protocol_name = "lws-minimal-client";
|
||||
i.pwsi = &mco->wsi;
|
||||
i.retry_and_idle_policy = &retry;
|
||||
i.userdata = mco;
|
||||
|
||||
if (!lws_client_connect_via_info(&i))
|
||||
/*
|
||||
* Failed... schedule a retry... we can't use the _retry_wsi()
|
||||
* convenience wrapper api here because no valid wsi at this
|
||||
* point.
|
||||
*/
|
||||
if (lws_retry_sul_schedule(context, 0, sul, &retry,
|
||||
connect_client, &mco->retry_count)) {
|
||||
lwsl_err("%s: connection attempts exhausted\n", __func__);
|
||||
interrupted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
callback_minimal(struct lws *wsi, enum lws_callback_reasons reason,
|
||||
void *user, void *in, size_t len)
|
||||
{
|
||||
struct my_conn *mco = (struct my_conn *)user;
|
||||
|
||||
switch (reason) {
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
||||
lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
|
||||
in ? (char *)in : "(null)");
|
||||
goto do_retry;
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||
lwsl_hexdump_notice(in, len);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||
lwsl_user("%s: established\n", __func__);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||
goto do_retry;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return lws_callback_http_dummy(wsi, reason, user, in, len);
|
||||
|
||||
do_retry:
|
||||
/*
|
||||
* retry the connection to keep it nailed up
|
||||
*
|
||||
* For this example, we try to conceal any problem for one set of
|
||||
* backoff retries and then exit the app.
|
||||
*
|
||||
* If you set retry.conceal_count to be larger than the number of
|
||||
* elements in the backoff table, it will never give up and keep
|
||||
* retrying at the last backoff delay plus the random jitter amount.
|
||||
*/
|
||||
if (lws_retry_sul_schedule_retry_wsi(wsi, &mco->sul, connect_client,
|
||||
&mco->retry_count)) {
|
||||
lwsl_err("%s: connection attempts exhausted\n", __func__);
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct lws_protocols protocols[] = {
|
||||
{ "lws-minimal-client", callback_minimal, 0, 0, },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
sigint_handler(int sig)
|
||||
{
|
||||
interrupted = 1;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
const char *p;
|
||||
int n = 0;
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
memset(&info, 0, sizeof info);
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
|
||||
lwsl_user("LWS minimal ws client\n");
|
||||
|
||||
info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||
info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
|
||||
info.protocols = protocols;
|
||||
|
||||
#if defined(LWS_WITH_MBEDTLS)
|
||||
/*
|
||||
* OpenSSL uses the system trust store. mbedTLS has to be told which
|
||||
* CA to trust explicitly.
|
||||
*/
|
||||
info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
|
||||
#endif
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "--protocol")))
|
||||
pro = p;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-s")))
|
||||
server_address = p;
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-p")))
|
||||
port = atoi(p);
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-j"))
|
||||
ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-k"))
|
||||
ssl_connection |= LCCSCF_ALLOW_INSECURE;
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-m"))
|
||||
ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
|
||||
|
||||
if (lws_cmdline_option(argc, argv, "-e"))
|
||||
ssl_connection |= LCCSCF_ALLOW_EXPIRED;
|
||||
|
||||
info.fd_limit_per_thread = 1 + 1 + 1;
|
||||
|
||||
context = lws_create_context(&info);
|
||||
if (!context) {
|
||||
lwsl_err("lws init failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* schedule the first client connection attempt to happen immediately */
|
||||
lws_sul_schedule(context, 0, &mco.sul, connect_client, 1);
|
||||
|
||||
while (n >= 0 && !interrupted)
|
||||
n = lws_service(context, 0);
|
||||
|
||||
lws_context_destroy(context);
|
||||
lwsl_user("Completed\n");
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue