Websocket Client

The IWebsocketClient interface is for working with websocket connections. While websocket is an update to an existing HTTP connection, there is no need to pass an existing HTTP connection to IWebsocketClient. Everything will handled from the interface itself. To establish a connection, the address of the websocket server need to be passed with the leading ws:// prefix. If the connection need to be encrypted, wss:// should be used instead. The server can be passed as TCP (v4 or v6) address or as name, the IWebsocketClient tries to resolve. Note that the IWebsocketClient only handles the client side of the connection. For implement a server-side websocket connection, see IWebserverPlugin - IWebsocket.

To create an IWebsocketClient instance, the static Create() function need to be called. Two existing ISocketProvider as well as an IDns instance must be passed to Create(). Those interfaces must not be deleted before releasing the IWebsocketClient instance!

File information

Filecommon/interface/websocket_client.h

Classes IWebsocketClient
UWebsocketClient

Data types IWebsocketClient::closereason_t
WEBSOCKET_MAX_PAYLOAD_SIZE

Examples IWebsocketClient

Classes

IWebsocketClient

class IWebsocketClient {
public:
    static IWebsocketClient * Create(class IIoMux * const iomux,
                                     class UWebsocketClient * const user,
                                     class ISocketProvider * const tcpSocketProvider,
                                     class ISocketProvider * const tlsSocketProvider,
                                     class IDns * const dns,
                                     class IInstanceLog * const log);
	
    virtual ~IWebsocketClient() {}
    virtual void Connect(const char * uri);
    virtual void Close();
    virtual void Send(const void * buf, size_t len, bool text = true);
    virtual void Recv(void * buf = nullptr, size_t len = 0);
};

Overview

This is the interface to entablish a websocket connection. The interface is quite simple and straight forward to use.

Logging

To enable logging for IWebsocketClient, the flag LOG_WEBSOCKET_CLIENT must be set in the managers diagnostic settings.

Public functions

Create (static function)
This static function creates the IWebsocketInstance. The returned instance must be release if no longer needed by using the C++ delete operator.

Parameters

class IIoMux * const iomuxThe IIoMux instance the IWebsocketClient instance will be registered to.
class UWebsocketClient * const userThe UWebsocketClient instance that will receive the callbacks of IWebsocketClient.
class ISocketProvider * const tcpSocketProviderA ISocketProvider instance that can create a TCP ISocket.
class ISocketProvider * const tlsSocketProviderA ISocketProvider instance that can create a TLS ISocket.
class IDns * const dnsThe IDns instance used to resolve the server name if needed.
class IInstanceLog * const logThe IInstanceLog instance used for loging purposes.

Return value

The IWebsocketClient instance created. It must be freed by the C++ operator delete, if no longer be used.

Remarks

Note that the two ISocketProvider instances as well as the IDns instance must not be freed before the IWebsocketClient instance had been deleted.
Connect
Tries to connect to the given address. The address must be given in the default websocket url format. That means "ws://<address>" for a normal connection, "wss://<address>" for an ecrypted one. The IWebsocketClient can handle IPv4 and IPv6 addresses, as well as a server name which will be resolved using the IDns instance.

Parameters

const char * uriThe address to connect to.

Callbacks

On success, UWebsocketClient::WebsocketConnectComplete() will be called. If an error occurres, UWebsocketClient::WebsocketCloseComplete() will be called. Details of that error can be found inside the log, if logging for the websocket client is activated. Reason can be one of the following values:
WSCR_CONNECTION_FAILEDAn error occured while trying to connect to the websocket server. Details of that error can be found inside the log, if logging for the websocket client is enabled.
WSCR_URI_INVALIDThe address, passed to IWebsocketClient::Connect(), has a wrong format.
Close
Closes the websocket connection. If there is any data still cached and need to be sent, the data will be send before closing the connection.

Callbacks

After the connection had been closed, UWebsocketClient::WebsocketCloseComplete() will be called.
Send
Sends the given data to the wensocket server, IWebsocketClient is connected to. If the data could not be send right now, it will be cached and send later. The UWebsocketClient instance will be informed if the data had ben finally send.

Parameters

const void * bufThe buffer with the data to send. It must not be nullptr.
size_t lenThe number of bytes to send. The in buf given buffer must have at least a size of len bytes.
bool text(Default true) If set to false, the data will be send as binary data, else as text data.

Callbacks

After the data had been send, UWebsocketClient::WebsocketSendResult() will be called.
Recv
Tells the IWebsocketClient instance to receive data. The data will be written to the given buffer. The websocket client will read data to the given buffer, until the number of bytes passed had been received. Although the IWebsocketClient won't support partitial read, at least a nullptr (and a len of 0) can be passed to Recv(). This leads to a callback to ask for a buffer with the size needed. Doing this allows to make some kind of partitial read and comes in handy, if the size of data to receive is unkown. See UWebsocketClient::WebsocketRecvBuffer for more information.

Parameters

void * buf(Default nullptr) The buffer to read the data to.
size_t len(Default 0) The number of bytes to receive. The in buf given buffer must have at least a size of len bytes. This parameter will be ignored if buf = nullptr.

Callbacks

On success, UWebsocketClient::WebsocketRecvResult() will be called after data had been received. If an error occurres, UWebsocketClient::WebsocketCloseComplete() will be called. Details of that error can be found inside the log, if logging for the websocket client is activated. Reason can be one of the following values:
WSCR_BUFFER_OVERFLOWA websocket frame had been received, that exceeded the internal limit of frame size. See WEBSOCKET_MAX_PAYLOAD_SIZE for details.
WSCR_PROTOCOL_ERRORSomething went wrong during the weboscket communication. Details of that error can be found inside the log, if logging for the websocket client is enabled.

Remarks

When calling Recv with a buffer and a len, IWebsocketClient will wait until the requested amount of bytes had been received. So if the size of data to receive is unkown, it will be better to call Recv() wil nullptr as buffer and use the UWebsocketClient::WebsocketRecvBuffer() mechanism to receive the data.

UWebsocketClient

class UWebsocketClient {
public:
    virtual ~UWebsocketClient() {}
    virtual void WebsocketConnectComplete(IWebsocketClient * const websocket);
    virtual void WebsocketCloseComplete(IWebsocketClient * const websocket, closereason_t reason) ;
    virtual void WebsocketSendResult(IWebsocketClient * const websocket);
    virtual void WebsocketRecvResult(IWebsocketClient * const websocket, void * buf, size_t len, bool text, bool isFragment);
    virtual void * WebsocketRecvBuffer(size_t len) { return nullptr; }
};

Overview

The UWebsocketClient class is used to receive callbacks from an IWebsocketClient instance. An application must subclass UWebsocketClient, implement the functions that must be implemented and pass that class as user to IWebsocketClient::Create(). The instance of that subclass must not be freed before the IWebsocketClient instance assigned to. One UWebsocketClient instance can be assigned to multiple IWebsocketClient instances, because the calling IWebsocketClient will be passed as parameter to the callback functions.

Public functions

WebsocketConnectComplete
Will be called, after calling IWebsocketClient::Connect() and after the connection had been established. After receiving that callback, the IWebsocketClient instance can be used to send and receive data.

Parameters

class IWebsocketClient * const websocketThe calling IWebsocketClient instance.
WebsocketCloseComplete
Will be called after the websocket connection had been closed. That can be because of a call to IWebsocketClient::Close(), because the other side closed the connection or because of an error. The reason for the close will be passed as parameter.

Parameters

class IWebsocketClient * const websocketThe calling IWebsocketClient instance.
closereason_t reasonThe reason why the websocket connection had been closed. See IWebsocketClient::closereason_t for more details.
WebsocketSendResult
When calling IWebsocketClient::Send(), after sending the data UWebsocketClient::WebsocketSendResult() will be called. It is guaranteed, that the callback would not be called before returning from IWebsocketClient::Send().

Parameters

class IWebsocketClient * const websocketThe calling IWebsocketClient instance.
WebsocketRecvResult
After calling IWebsocketClient::Recv(), UWebsocketClient::WebsocketRecvResult() will be called when data had been received. Because websocket messages can be fragmented, that information will be passed to the callback function by the isFragmented flag. So to make sure that the whole message had been received, the application should call IWebsocketClient::Recv() until UWebsocketClient::WebsocketRecvResult() will be called with isFragmented set to false.

Parameters

class IWebsocketClient * const websocketThe calling IWebsocketClient instance.
void * bufThe buffer holding the data received. Will be the same as passed to IWebsocketClient::Recv() or returned by UWebsocketClient::WebsocketRecvResult().
size_t lenThe length of the data inside the buffer.
bool isFragementedIf true, the data is fragmented. That means, that there are still other parts of the data to receive. So isFragmented set to false can also intepreted as "all data received".

Data types

UWebsocketClient::closereason_t

Overview

When the web socket connection had been closed, UWebsocketClient::WebsocketCloseComplete() will be called with the reason for closing the connection. Note that the datatype is embedded in the UWebsocketClient class.

Values

WSCR_NORMAL_CLOSEThe conenction had been closed because of a call to IWebsocketClient::Close().
WSCR_CONNECTION_FAILEDAn error occured while trying to connect to the websocket server. Details of that error can be found inside the log, if logging for the websocket client is enabled.
WSCR_URI_INVALIDThe address, passed to IWebsocketClient::Connect(), has a wrong format.
WSCR_BUFFER_OVERFLOWA websocket frame had been received, that exceeded the internal limit of frame size. See WEBSOCKET_MAX_PAYLOAD_SIZE for details.
WSCR_PROTOCOL_ERRORSomething went wrong during the weboscket communication. Details of that error can be found inside the log, if logging for the websocket client is enabled.
WSCR_SOCKET_LOSTThe socket got lost unexpected. Details of that error can be found inside the log, if logging for the websocket client is enabled.
WSCR_SERVER_CLOSEDThe socket connection had been close from the server side.

WEBSOCKET_MAX_PAYLOAD_SIZE (define)

Overview

While the IWebsocketClient supports the full websocket protocol, the size of one websocket frame is limited. This limit is defined by WEBSOCKET_MAX_PAYLOAD_SIZE. If a frame comes in with a data size greated than WEBSOCKET_MAX_PAYLOAD_SIZE, UWebsocketClose::WebsocketCloseComplete() will be called with reason set to WSCR_BUFFER_OVERFLOW.

Values

WEBSOCKET_MAX_PAYLOAD_SIZEValue in bytes, defined to 16384

Code Example

IWebsocketClient

app::app(class IIoMux * iomux,
         ISocketProvider * tcpSocketProvider,
         ISocketProvider * tlsSocketProvider,
         IDns * dns)
    : iomux(iomux)
{
    this->websocketClient = IWebsocketClient::Create(iomux, this, tcpSocketProvider, tlsSocketProvider, dns);
    this->websocketClient->Connect("wss://myserver.com/websocket");
}

void app::WebsocketConnectComplete(IWebsocketClient * const websocket)
{
    printf("Websocket connected\n");
	this->websocketClient->Recv();
}

void app::WebsocketCloseComplete(IWebsocketClient * const websocket, closereason_t reason)
{
    printf("Websocket connection closed\n");
    delete this->websocketClient;
    this->websocketClient = NULL;
    iomux->Terminate();
}

void app::WebsocketSendResult(IWebsocketClient * const websocket)
{
    this->websocketClient->Close();
}

void * app::WebsocketRecvBuffer(size_t len)
{
    return malloc(len);
}

void app::WebsocketRecvResult(IWebsocketClient * const websocket, void * buf, size_t len, bool text)
{
    printf("Websocket client received data (text = %s)\n", (test ? "true" : "false"));
    debug->HexDump(buf, len);
    this->websocketClient->Send(buf, len);
	free(buf);
}