123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887 |
- Websockets are a genuine way to implement push notifications,
- where the server initiates the communication while the client can be idle.
- Usually a HTTP communication is half-duplex and always requested by the client,
- but websockets are full-duplex and only initialized by the client.
- In the further communication both sites can use the websocket at any time
- to send data to the other site.
- To initialize a websocket connection the client sends a special HTTP request
- to the server and initializes
- a handshake between client and server which switches from the HTTP protocol
- to the websocket protocol.
- Thus both the server as well as the client must support websockets.
- If proxys are used, they must support websockets too.
- In this chapter we take a look on server and client, but with a focus on
- the server with @emph{libmicrohttpd}.
- Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
- which is required for switching from the HTTP protocol.
- Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
- to support the websocket protocol.
- @heading Upgrading connections with libmicrohttpd
- To support websockets we need to enable upgrading of HTTP connections first.
- This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
- @code{MHD_start_daemon()}.
- @verbatim
- daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD |
- MHD_USE_THREAD_PER_CONNECTION |
- MHD_ALLOW_UPGRADE |
- MHD_USE_ERROR_LOG,
- PORT, NULL, NULL,
- &access_handler, NULL,
- MHD_OPTION_END);
- @end verbatim
- @noindent
- The next step is to turn a specific request into an upgraded connection.
- This done in our @code{access_handler} by calling
- @code{MHD_create_response_for_upgrade()}.
- An @code{upgrade_handler} will be passed to perform the low-level actions
- on the socket.
- @emph{Please note that the socket here is just a regular socket as provided
- by the operating system.
- To use it as a websocket, some more steps from the following
- chapters are required.}
- @verbatim
- static enum MHD_Result
- access_handler (void *cls,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *version,
- const char *upload_data,
- size_t *upload_data_size,
- void **ptr)
- {
- /* ... */
- /* some code to decide whether to upgrade or not */
- /* ... */
- /* create the response for upgrade */
- response = MHD_create_response_for_upgrade (&upgrade_handler,
- NULL);
- /* ... */
- /* additional headers, etc. */
- /* ... */
- ret = MHD_queue_response (connection,
- MHD_HTTP_SWITCHING_PROTOCOLS,
- response);
- MHD_destroy_response (response);
- return ret;
- }
- @end verbatim
- @noindent
- In the @code{upgrade_handler} we receive the low-level socket,
- which is used for the communication with the specific client.
- In addition to the low-level socket we get:
- @itemize @bullet
- @item
- Some data, which has been read too much while @emph{libmicrohttpd} was
- switching the protocols.
- This value is usually empty, because it would mean that the client
- has sent data before the handshake was complete.
- @item
- A @code{struct MHD_UpgradeResponseHandle} which is used to perform
- special actions like closing, corking or uncorking the socket.
- These commands are executed by passing the handle
- to @code{MHD_upgrade_action()}.
- @end itemize
- Depending of the flags specified while calling @code{MHD_start_deamon()}
- our @code{upgrade_handler} is either executed in the same thread
- as our daemon or in a thread specific for each connection.
- If it is executed in the same thread then @code{upgrade_handler} is
- a blocking call for our webserver and
- we should finish it as fast as possible (i. e. by creating a thread and
- passing the information there).
- If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
- @code{MHD_start_daemon()} then a separate thread is used and
- thus our @code{upgrade_handler} needs not to start a separate thread.
- An @code{upgrade_handler}, which is called with a separate thread
- per connection, could look like this:
- @verbatim
- static void
- upgrade_handler (void *cls,
- struct MHD_Connection *connection,
- void *req_cls,
- const char *extra_in,
- size_t extra_in_size,
- MHD_socket fd,
- struct MHD_UpgradeResponseHandle *urh)
- {
- /* ... */
- /* do something with the socket `fd` like `recv()` or `send()` */
- /* ... */
- /* close the socket when it is not needed anymore */
- MHD_upgrade_action (urh,
- MHD_UPGRADE_ACTION_CLOSE);
- }
- @end verbatim
- @noindent
- This is all you need to know for upgrading connections
- with @emph{libmicrohttpd}.
- The next chapters focus on using the websocket protocol
- with @emph{libmicrohttpd_ws}.
- @heading Websocket handshake with libmicrohttpd_ws
- To request a websocket connection the client must send
- the following information with the HTTP request:
- @itemize @bullet
- @item
- A @code{GET} request must be sent.
- @item
- The version of the HTTP protocol must be 1.1 or higher.
- @item
- A @code{Host} header field must be sent
- @item
- A @code{Upgrade} header field containing the keyword "websocket"
- (case-insensitive).
- Please note that the client could pass multiple protocols separated by comma.
- @item
- A @code{Connection} header field that includes the token "Upgrade"
- (case-insensitive).
- Please note that the client could pass multiple tokens separated by comma.
- @item
- A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
- The decoded the value is 16 bytes long
- and has been generated randomly by the client.
- @item
- A @code{Sec-WebSocket-Version} header field with the value "13".
- @end itemize
- Optionally the client can also send the following information:
- @itemize @bullet
- @item
- A @code{Origin} header field can be used to determine the source
- of the client (i. e. the website).
- @item
- A @code{Sec-WebSocket-Protocol} header field can contain a list
- of supported protocols by the client, which can be sent over the websocket.
- @item
- A @code{Sec-WebSocket-Extensions} header field which may contain extensions
- to the websocket protocol. The extensions must be registered by IANA.
- @end itemize
- A valid example request from the client could look like this:
- @verbatim
- GET /chat HTTP/1.1
- Host: server.example.com
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
- Sec-WebSocket-Version: 13
- @end verbatim
- @noindent
- To complete the handshake the server must respond with
- some specific response headers:
- @itemize @bullet
- @item
- The HTTP response code @code{101 Switching Protocols} must be answered.
- @item
- An @code{Upgrade} header field containing the value "websocket" must be sent.
- @item
- A @code{Connection} header field containing the value "Upgrade" must be sent.
- @item
- A @code{Sec-WebSocket-Accept} header field containing a value, which
- has been calculated from the @code{Sec-WebSocket-Key} request header field,
- must be sent.
- @end itemize
- Optionally the server may send following headers:
- @itemize @bullet
- @item
- A @code{Sec-WebSocket-Protocol} header field containing a protocol
- of the list specified in the corresponding request header field.
- @item
- A @code{Sec-WebSocket-Extension} header field containing all used extensions
- of the list specified in the corresponding request header field.
- @end itemize
- A valid websocket HTTP response could look like this:
- @verbatim
- HTTP/1.1 101 Switching Protocols
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- @end verbatim
- @noindent
- To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
- some helper functions for the @code{access_handler} callback function:
- @itemize @bullet
- @item
- @code{MHD_websocket_check_http_version()} checks whether the HTTP version
- is 1.1 or above.
- @item
- @code{MHD_websocket_check_connection_header()} checks whether the value
- of the @code{Connection} request header field contains
- an "Upgrade" token (case-insensitive).
- @item
- @code{MHD_websocket_check_upgrade_header()} checks whether the value
- of the @code{Upgrade} request header field contains
- the "websocket" keyword (case-insensitive).
- @item
- @code{MHD_websocket_check_version_header()} checks whether the value
- of the @code{Sec-WebSocket-Version} request header field is "13".
- @item
- @code{MHD_websocket_create_accept_header()} takes the value from
- the @code{Sec-WebSocket-Key} request header and calculates the value
- for the @code{Sec-WebSocket-Accept} response header field.
- @end itemize
- The @code{access_handler} example of the previous chapter can now be
- extended with these helper functions to perform the websocket handshake:
- @verbatim
- static enum MHD_Result
- access_handler (void *cls,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *version,
- const char *upload_data,
- size_t *upload_data_size,
- void **ptr)
- {
- static int aptr;
- struct MHD_Response *response;
- int ret;
- (void) cls; /* Unused. Silent compiler warning. */
- (void) upload_data; /* Unused. Silent compiler warning. */
- (void) upload_data_size; /* Unused. Silent compiler warning. */
- if (0 != strcmp (method, "GET"))
- return MHD_NO; /* unexpected method */
- if (&aptr != *ptr)
- {
- /* do never respond on first call */
- *ptr = &aptr;
- return MHD_YES;
- }
- *ptr = NULL; /* reset when done */
- if (0 == strcmp (url, "/"))
- {
- /* Default page for visiting the server */
- struct MHD_Response *response = MHD_create_response_from_buffer (
- strlen (PAGE),
- PAGE,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- response);
- MHD_destroy_response (response);
- }
- else if (0 == strcmp (url, "/chat"))
- {
- char is_valid = 1;
- const char* value = NULL;
- char sec_websocket_accept[29];
- if (0 != MHD_websocket_check_http_version (version))
- {
- is_valid = 0;
- }
- value = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONNECTION);
- if (0 != MHD_websocket_check_connection_header (value))
- {
- is_valid = 0;
- }
- value = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_UPGRADE);
- if (0 != MHD_websocket_check_upgrade_header (value))
- {
- is_valid = 0;
- }
- value = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
- if (0 != MHD_websocket_check_version_header (value))
- {
- is_valid = 0;
- }
- value = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
- if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
- {
- is_valid = 0;
- }
- if (1 == is_valid)
- {
- /* upgrade the connection */
- response = MHD_create_response_for_upgrade (&upgrade_handler,
- NULL);
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CONNECTION,
- "Upgrade");
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_UPGRADE,
- "websocket");
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
- sec_websocket_accept);
- ret = MHD_queue_response (connection,
- MHD_HTTP_SWITCHING_PROTOCOLS,
- response);
- MHD_destroy_response (response);
- }
- else
- {
- /* return error page */
- struct MHD_Response*response = MHD_create_response_from_buffer (
- strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
- PAGE_INVALID_WEBSOCKET_REQUEST,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_BAD_REQUEST,
- response);
- MHD_destroy_response (response);
- }
- }
- else
- {
- struct MHD_Response*response = MHD_create_response_from_buffer (
- strlen (PAGE_NOT_FOUND),
- PAGE_NOT_FOUND,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NOT_FOUND,
- response);
- MHD_destroy_response (response);
- }
- return ret;
- }
- @end verbatim
- @noindent
- Please note that we skipped the check of the Host header field here,
- because we don't know the host for this example.
- @heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
- Once the websocket connection is established you can receive/send frame data
- with the low-level socket functions @code{recv()} and @code{send()}.
- The frame data which goes over the low-level socket is encoded according
- to the websocket protocol.
- To use received payload data, you need to decode the frame data first.
- To send payload data, you need to encode it into frame data first.
- @emph{libmicrohttpd_ws} provides several functions for encoding of
- payload data and decoding of frame data:
- @itemize @bullet
- @item
- @code{MHD_websocket_decode()} decodes received frame data.
- The payload data may be of any kind, depending upon what the client has sent.
- So this decode function is used for all kind of frames and returns
- the frame type along with the payload data.
- @item
- @code{MHD_websocket_encode_text()} encodes text.
- The text must be encoded with UTF-8.
- @item
- @code{MHD_websocket_encode_binary()} encodes binary data.
- @item
- @code{MHD_websocket_encode_ping()} encodes a ping request to
- check whether the websocket is still valid and to test latency.
- @item
- @code{MHD_websocket_encode_ping()} encodes a pong response to
- answer a received ping request.
- @item
- @code{MHD_websocket_encode_close()} encodes a close request.
- @item
- @code{MHD_websocket_free()} frees data returned by the encode/decode functions.
- @end itemize
- Since you could receive or send fragmented data (i. e. due to a too
- small buffer passed to @code{recv}) all of these encode/decode
- functions require a pointer to a @code{struct MHD_WebSocketStream} passed
- as argument.
- In this structure @emph{libmicrohttpd_ws} stores information
- about encoding/decoding of the particular websocket.
- For each websocket you need a unique @code{struct MHD_WebSocketStream}
- to encode/decode with this library.
- To create or destroy @code{struct MHD_WebSocketStream}
- we have additional functions:
- @itemize @bullet
- @item
- @code{MHD_websocket_stream_init()} allocates and initializes
- a new @code{struct MHD_WebSocketStream}.
- You can specify some options here to alter the behavior of the websocket stream.
- @item
- @code{MHD_websocket_stream_free()} frees a previously allocated
- @code{struct MHD_WebSocketStream}.
- @end itemize
- With these encode/decode functions we can improve our @code{upgrade_handler}
- callback function from an earlier example to a working websocket:
- @verbatim
- static void
- upgrade_handler (void *cls,
- struct MHD_Connection *connection,
- void *req_cls,
- const char *extra_in,
- size_t extra_in_size,
- MHD_socket fd,
- struct MHD_UpgradeResponseHandle *urh)
- {
- /* make the socket blocking (operating-system-dependent code) */
- make_blocking (fd);
- /* create a websocket stream for this connection */
- struct MHD_WebSocketStream* ws;
- int result = MHD_websocket_stream_init (&ws,
- 0,
- 0);
- if (0 != result)
- {
- /* Couldn't create the websocket stream.
- * So we close the socket and leave
- */
- MHD_upgrade_action (urh,
- MHD_UPGRADE_ACTION_CLOSE);
- return;
- }
- /* Let's wait for incoming data */
- const size_t buf_len = 256;
- char buf[buf_len];
- ssize_t got;
- while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
- {
- got = recv (fd,
- buf,
- buf_len,
- 0);
- if (0 >= got)
- {
- /* the TCP/IP socket has been closed */
- break;
- }
- /* parse the entire received data */
- size_t buf_offset = 0;
- while (buf_offset < (size_t) got)
- {
- size_t new_offset = 0;
- char *frame_data = NULL;
- size_t frame_len = 0;
- int status = MHD_websocket_decode (ws,
- buf + buf_offset,
- ((size_t) got) - buf_offset,
- &new_offset,
- &frame_data,
- &frame_len);
- if (0 > status)
- {
- /* an error occurred and the connection must be closed */
- if (NULL != frame_data)
- {
- MHD_websocket_free (ws, frame_data);
- }
- break;
- }
- else
- {
- buf_offset += new_offset;
- if (0 < status)
- {
- /* the frame is complete */
- switch (status)
- {
- case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
- /* The client has sent some text.
- * We will display it and answer with a text frame.
- */
- if (NULL != frame_data)
- {
- printf ("Received message: %s\n", frame_data);
- MHD_websocket_free (ws, frame_data);
- frame_data = NULL;
- }
- result = MHD_websocket_encode_text (ws,
- "Hello",
- 5, /* length of "Hello" */
- 0,
- &frame_data,
- &frame_len,
- NULL);
- if (0 == result)
- {
- send_all (fd,
- frame_data,
- frame_len);
- }
- break;
- case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
- /* if we receive a close frame, we will respond with one */
- MHD_websocket_free (ws,
- frame_data);
- frame_data = NULL;
- result = MHD_websocket_encode_close (ws,
- 0,
- NULL,
- 0,
- &frame_data,
- &frame_len);
- if (0 == result)
- {
- send_all (fd,
- frame_data,
- frame_len);
- }
- break;
- case MHD_WEBSOCKET_STATUS_PING_FRAME:
- /* if we receive a ping frame, we will respond */
- /* with the corresponding pong frame */
- {
- char *pong = NULL;
- size_t pong_len = 0;
- result = MHD_websocket_encode_pong (ws,
- frame_data,
- frame_len,
- &pong,
- &pong_len);
- if (0 == result)
- {
- send_all (fd,
- pong,
- pong_len);
- }
- MHD_websocket_free (ws,
- pong);
- }
- break;
- default:
- /* Other frame types are ignored
- * in this minimal example.
- * This is valid, because they become
- * automatically skipped if we receive them unexpectedly
- */
- break;
- }
- }
- if (NULL != frame_data)
- {
- MHD_websocket_free (ws, frame_data);
- }
- }
- }
- }
- /* free the websocket stream */
- MHD_websocket_stream_free (ws);
- /* close the socket when it is not needed anymore */
- MHD_upgrade_action (urh,
- MHD_UPGRADE_ACTION_CLOSE);
- }
- /* This helper function is used for the case that
- * we need to resend some data
- */
- static void
- send_all (MHD_socket fd,
- const char *buf,
- size_t len)
- {
- ssize_t ret;
- size_t off;
- for (off = 0; off < len; off += ret)
- {
- ret = send (fd,
- &buf[off],
- (int) (len - off),
- 0);
- if (0 > ret)
- {
- if (EAGAIN == errno)
- {
- ret = 0;
- continue;
- }
- break;
- }
- if (0 == ret)
- break;
- }
- }
- /* This helper function contains operating-system-dependent code and
- * is used to make a socket blocking.
- */
- static void
- make_blocking (MHD_socket fd)
- {
- #if defined(MHD_POSIX_SOCKETS)
- int flags;
- flags = fcntl (fd, F_GETFL);
- if (-1 == flags)
- return;
- if ((flags & ~O_NONBLOCK) != flags)
- if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
- abort ();
- #elif defined(MHD_WINSOCK_SOCKETS)
- unsigned long flags = 0;
- ioctlsocket (fd, FIONBIO, &flags);
- #endif /* MHD_WINSOCK_SOCKETS */
- }
- @end verbatim
- @noindent
- Please note that the websocket in this example is only half-duplex.
- It waits until the blocking @code{recv()} call returns and
- only does then something.
- In this example all frame types are decoded by @emph{libmicrohttpd_ws},
- but we only do something when a text, ping or close frame is received.
- Binary and pong frames are ignored in our code.
- This is legit, because the server is only required to implement at
- least support for ping frame or close frame (the other frame types
- could be skipped in theory, because they don't require an answer).
- The pong frame doesn't require an answer and whether text frames or
- binary frames get an answer simply belongs to your server application.
- So this is a valid minimal example.
- Until this point you've learned everything you need to basically
- use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
- These libraries offer much more functions for some specific cases.
- The further chapters of this tutorial focus on some specific problems
- and the client site programming.
- @heading Using full-duplex websockets
- To use full-duplex websockets you can simply create two threads
- per websocket connection.
- One of these threads is used for receiving data with
- a blocking @code{recv()} call and the other thread is triggered
- by the application internal codes and sends the data.
- A full-duplex websocket example is implemented in the example file
- @code{websocket_chatserver_example.c}.
- @heading Error handling
- The most functions of @emph{libmicrohttpd_ws} return a value
- of @code{enum MHD_WEBSOCKET_STATUS}.
- The values of this enumeration can be converted into an integer
- and have an easy interpretation:
- @itemize @bullet
- @item
- If the value is less than zero an error occurred and the call has failed.
- Check the enumeration values for more specific information.
- @item
- If the value is equal to zero, the call succeeded.
- @item
- If the value is greater than zero, the call succeeded and the value
- specifies the decoded frame type.
- Currently positive values are only returned by @code{MHD_websocket_decode()}
- (of the functions with this return enumeration type).
- @end itemize
- A websocket stream can also get broken when invalid frame data is received.
- Also the other site could send a close frame which puts the stream into
- a state where it may not be used for regular communication.
- Whether a stream has become broken, can be checked with
- @code{MHD_websocket_stream_is_valid()}.
- @heading Fragmentation
- In addition to the regular TCP/IP fragmentation the websocket protocol also
- supports fragmentation.
- Fragmentation could be used for continuous payload data such as video data
- from a webcam.
- Whether or not you want to receive fragmentation is specified upon
- initialization of the websocket stream.
- If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
- of @code{MHD_websocket_stream_init()} then you can receive fragments.
- If you don't pass this flag (in the most cases you just pass zero as flags)
- then you don't want to handle fragments on your own.
- @emph{libmicrohttpd_ws} removes then the fragmentation for you
- in the background.
- You only get the completely assembled frames.
- Upon encoding you specify whether or not you want to create a fragmented frame
- by passing a flag to the corresponding encode function.
- Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
- can be used for fragmentation, because the other frame types may
- not be fragmented.
- Encoding fragmented frames is independent of
- the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
- @heading Quick guide to websockets in JavaScript
- Websockets are supported in all modern web browsers.
- You initialize a websocket connection by creating an instance of
- the @code{WebSocket} class provided by the web browser.
- There are some simple rules for using websockets in the browser:
- @itemize @bullet
- @item
- When you initialize the instance of the websocket class you must pass an URL.
- The URL must either start with @code{ws://}
- (for not encrypted websocket protocol) or @code{wss://}
- (for TLS-encrypted websocket protocol).
- @strong{IMPORTANT:} If your website is accessed via @code{https://}
- then you are in a security context, which means that you are only allowed to
- access other secure protocols.
- So you can only use @code{wss://} for websocket connections then.
- If you try to @code{ws://} instead then your websocket connection will
- automatically fail.
- @item
- The WebSocket class uses events to handle the receiving of data.
- JavaScript is per definition a single-threaded language so
- the receiving events will never overlap.
- Sending is done directly by calling a method of the instance of
- the WebSocket class.
- @end itemize
- Here is a short example for receiving/sending data to the same host
- as the website is running on:
- @verbatim
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title>Websocket Demo</title>
- <script>
- let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
- window.location.host + '/chat';
- let socket = null;
- window.onload = function(event) {
- socket = new WebSocket(url);
- socket.onopen = function(event) {
- document.write('The websocket connection has been established.<br>');
- // Send some text
- socket.send('Hello from JavaScript!');
- }
- socket.onclose = function(event) {
- document.write('The websocket connection has been closed.<br>');
- }
- socket.onerror = function(event) {
- document.write('An error occurred during the websocket communication.<br>');
- }
- socket.onmessage = function(event) {
- document.write('Websocket message received: ' + event.data + '<br>');
- }
- }
- </script>
- </head>
- <body>
- </body>
- </html>
- @end verbatim
- @noindent
|