Biblioteka Websockets działa dobrze w C++ udostępniając mechanizm komunikacji za pomocą websockets (protokół na www do obustronnej komunikacji klient-serwer).
Ten przykład pokazuje połączenie dwóch przykładowych aplikacji będących załącznikami do bibliotek.
Pierwszy przykład pochodzi z biblioteki LIBWEBSOCKETS, aby ją uruchomić należy najpierw zainstalować i skompilować poleceniami
1 2 3 4 | sudo apt-get install libssl-dev cmake . make sudo make install |
.
Jeśli programy nie chcą się uruchomić trzeba dodać ścieżkę do biblioteki do ścieżek systemowych bibliotek
1 | export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib |
Programy kompilujemy z biblioteką -lwebsockets
1 | g++ minimal-ws-server.c -o minimalserv -fpermissive -lwebsockets |
Potem uruchamiamy nowy plik
1 | ./minimalserv |
W katalogu libwebsockets/minimal-examples/ws-server/ jest przykład z którego pochodzi ten kod:
minimal-ws-server.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | /* * lws-minimal-ws-server * * Written in 2010-2019 by Andy Green <andy@warmcat.com> * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * * This demonstrates the most minimal http server you can make with lws, * with an added websocket chat server. * * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of * the directory it was started in. * You can change that by changing mount.origin. */ #include <libwebsockets.h> #include <string.h> #include <signal.h> #include <iostream> using namespace std; #define LWS_PLUGIN_STATIC #include "protocol_lws_minimal.c" static struct lws_protocols protocols[] = { { "http", lws_callback_http_dummy, 0, 0 }, LWS_PLUGIN_PROTOCOL_MINIMAL, { NULL, NULL, 0, 0 } /* terminator */ }; static int interrupted; static const struct lws_http_mount mount = { /* .mount_next */ NULL, /* linked-list "next" */ /* .mountpoint */ "/", /* mountpoint URL */ /* .origin */ "./mount-origin", /* serve from dir */ /* .def */ "index.html", /* default filename */ /* .protocol */ NULL, /* .cgienv */ NULL, /* .extra_mimetypes */ NULL, /* .interpret */ NULL, /* .cgi_timeout */ 0, /* .cache_max_age */ 0, /* .auth_mask */ 0, /* .cache_reusable */ 0, /* .cache_revalidate */ 0, /* .cache_intermediaries */ 0, /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ /* .mountpoint_len */ 1, /* char count */ /* .basic_auth_login_file */ NULL, }; void sigint_handler(int sig) { interrupted = 1; } int main(int argc, const char **argv) { struct lws_context_creation_info info; struct lws_context *context; const char *p; int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE /* for LLL_ verbosity above NOTICE to be built into lws, * lws must have been configured and built with * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ /* | LLL_DEBUG */; cout << "start!\n"; signal(SIGINT, sigint_handler); if ((p = lws_cmdline_option(argc, argv, "-d"))) logs = atoi(p); lws_set_log_level(logs, NULL); lwsl_user("LWS minimal ws server | visit http://localhost:7681 (-s = use TLS / https)\n"); memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ info.port = 7681; info.mounts = &mount; info.protocols = protocols; info.vhost_name = "localhost"; info.ws_ping_pong_interval = 10; info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; if (lws_cmdline_option(argc, argv, "-s")) { lwsl_user("Server using TLS\n"); info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; info.ssl_cert_filepath = "localhost-100y.cert"; info.ssl_private_key_filepath = "localhost-100y.key"; } if (lws_cmdline_option(argc, argv, "-h")) info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; context = lws_create_context(&info); if (!context) { lwsl_err("lws init failed\n"); return 1; } while (n >= 0 && !interrupted) n = lws_service(context, 1000); lws_context_destroy(context); return 0; } |
i drugi plik z obsługą przerwania, protocol_lws_minimal.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /* * ws protocol handler plugin for "lws-minimal" * * Written in 2010-2019 by Andy Green <andy@warmcat.com> * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * * This version holds a single message at a time, which may be lost if a new * message comes. See the minimal-ws-server-ring sample for the same thing * but using an lws_ring ringbuffer to hold up to 8 messages at a time. */ #if !defined (LWS_PLUGIN_STATIC) #define LWS_DLL #define LWS_INTERNAL #include <libwebsockets.h> #endif #include <string.h> /* one of these created for each message */ struct msg { void *payload; /* is malloc'd */ size_t len; }; /* one of these is created for each client connecting to us */ struct per_session_data__minimal { struct per_session_data__minimal *pss_list; struct lws *wsi; int last; /* the last message number we sent */ }; /* one of these is created for each vhost our protocol is used with */ struct per_vhost_data__minimal { struct lws_context *context; struct lws_vhost *vhost; const struct lws_protocols *protocol; struct per_session_data__minimal *pss_list; /* linked-list of live pss*/ struct msg amsg; /* the one pending message... */ int current; /* the current message number we are caching */ }; /* destroys the message when everyone has had a copy of it */ static void __minimal_destroy_message(void *_msg) { struct msg *msg = _msg; free(msg->payload); msg->payload = NULL; msg->len = 0; } static int callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct per_session_data__minimal *pss = (struct per_session_data__minimal *)user; struct per_vhost_data__minimal *vhd = (struct per_vhost_data__minimal *) lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi)); string *cordovaMessage; char *cordovaTemp = new char[len+1]; //do konwersji void * > string * int m; cerr << "CALLBACK:"<<reason <<" \n"; switch (reason) { case LWS_CALLBACK_PROTOCOL_INIT: vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), lws_get_protocol(wsi), sizeof(struct per_vhost_data__minimal)); vhd->context = lws_get_context(wsi); vhd->protocol = lws_get_protocol(wsi); vhd->vhost = lws_get_vhost(wsi); cerr<< "----INIT\n"; break; case LWS_CALLBACK_ESTABLISHED: /* add ourselves to the list of live pss held in the vhd */ lws_ll_fwd_insert(pss, pss_list, vhd->pss_list); pss->wsi = wsi; pss->last = vhd->current; cerr<< "----ESTABLISHED\n"; break; case LWS_CALLBACK_CLOSED: /* remove our closing pss from the list of live pss */ lws_ll_fwd_remove(struct per_session_data__minimal, pss_list, pss, vhd->pss_list); cerr<< "----CLOSED\n"; break; case LWS_CALLBACK_SERVER_WRITEABLE: if (!vhd->amsg.payload) break; if (pss->last == vhd->current) break; /* notice we allowed for LWS_PRE in the payload already */ m = lws_write(wsi, ((unsigned char *)vhd->amsg.payload) + LWS_PRE, vhd->amsg.len, LWS_WRITE_TEXT); if (m < (int)vhd->amsg.len) { lwsl_err("ERROR %d writing to ws\n", m); return -1; } } pss->last = vhd->current; cerr<< "----WRITABLE\n"; break; case LWS_CALLBACK_RECEIVE: if (vhd->amsg.payload) __minimal_destroy_message(&vhd->amsg); vhd->amsg.len = len; /* notice we over-allocate by LWS_PRE */ vhd->amsg.payload = malloc(LWS_PRE + len); if (!vhd->amsg.payload) { lwsl_user("OOM: dropping\n"); break; } memcpy((char *)vhd->amsg.payload + LWS_PRE, in, len); //TU MOŻNA ODCZYTAĆ TRANSMISJĘ PRZYCHODZĄCĄ np. do zmiennej typu std::string * memcpy(cordovaTemp, in, len); //kopiowanie z void * do char * cordovaTemp[len] = '\0'; //dodanie znaku konca stringu (brakowalo) cordovaMessage = new string(cordovaTemp); cerr << "Got cordova message: " << *cordovaMessage << "\n"; delete cordovaMessage; //jak niepotrzebne - usuwamy //^ ////////////////////////////////////////////////////////////////////// vhd->current++; /* * let everybody know we want to write something on them * as soon as they are ready */ lws_start_foreach_llp(struct per_session_data__minimal **, ppss, vhd->pss_list) { lws_callback_on_writable((*ppss)->wsi); } lws_end_foreach_llp(ppss, pss_list); cerr<< "----RECEIVE\n"; break; case LWS_CALLBACK_ADD_HEADERS: cerr << "---HEADERS\n"; break; default: break; } delete[] cordovaTemp; return 0; } #define LWS_PLUGIN_PROTOCOL_MINIMAL \ { \ "lws-minimal", \ callback_minimal, \ sizeof(struct per_session_data__minimal), \ 128, \ 0, NULL, 0 \ } #if !defined (LWS_PLUGIN_STATIC) /* boilerplate needed if we are built as a dynamic plugin */ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_MINIMAL }; LWS_EXTERN LWS_VISIBLE int init_protocol_minimal(struct lws_context *context, struct lws_plugin_capability *c) { if (c->api_magic != LWS_PLUGIN_API_MAGIC) { lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, c->api_magic); return 1; } c->protocols = protocols; c->count_protocols = LWS_ARRAY_SIZE(protocols); c->extensions = NULL; c->count_extensions = 0; return 0; } LWS_EXTERN LWS_VISIBLE int destroy_protocol_minimal(struct lws_context *context) { return 0; } #endif |
Po drugiej stronie mamy klienta w Javascripcie (np. w Cordovie) z prostym kodem pochodzącym ze strony echo.websocket.org:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | //Tu wpisz adres IP serwera i port taki sam jak ustawiony w plikach wyżej: var wsUri = "ws://192.168.1.102:7681/";// "wss://echo.websocket.org/"; var output; function testWebSocket() { websocket = new WebSocket(wsUri, "lws-minimal"); //BARDZO WAŻNE: lws-minimal oznacza naszą bibliotekę z C++ i jej protokół websocket.onopen = function(evt) { onOpen(evt) }; websocket.onclose = function(evt) { onClose(evt) }; websocket.onmessage = function(evt) { onMessage(evt) }; websocket.onerror = function(evt) { onError(evt) }; } function onOpen(evt) { console.log("CONNECTED"); doSend("Hello"); } function onClose(evt) { console.log("DISCONNECTED"); } function onMessage(evt) { console.log('RESPONSE: ' + evt.data+''); websocket.close(); } function onError(evt) { console.log('<span style="color: red;">ERROR:</span> ' + evt.data); } function doSend(message) { console.log("SENT: " + message); websocket.send(message); } |
wywołujemy po załadowaniu strony (onload) funkcję
1 | testWebSockets(); |
po załadowaniu strony przejrzyjcie konsolę JAVASCRIPT, powinna wyglądać tak:
1 2 3 4 | CONNECTED (index):326 SENT: Hello (index):326 RESPONSE: Hello (index):326 DISCONNECTED |