Socket implementation

The socket interface is used to create socket for communication. There are four different types of sockets supported by the socket interfaces: normal TCP sockets, TLS sockets (which internally encapsulates a TCP socket), UDP sockets and finally local sockets, used for inter process communication. Each created ISocket instance can be used as server and client socket, depending on what will be called first: ISocket::Bind() or ISocket::Connect(). After calling one of theese functions, the mode the socket is defined and can not be changed anymore.

To create an ISocket instance, at first an instance of ISocketProvider must be created. This need to be done by calling one of the Create*SocketProvider. This defines the type of ISocketProvider as well as the type of ISocket that will be created. An ISocketProvider can be used to create multiple ISocket instances and can be deleted, if now longer be used. An created ISocket doesn't depend on the ISocketProvider used to created it.

Generally, eacht type of socket will be handled in the same way by ISocketProvider and ISocket, with two exceptions:

File information

Filecommon/interface/socket.h

Public functions CreateTCPSocketProvider
CreateTLSSocketProvider
CreateUDPSocketProvider
CreateLocalSocketProvider

Classes ISocketProvider
ISocketContext
ISocket
USocket

Data types shutdownreason_t

Examples ISocket - server
ISocket - client

Functions

Functions to initialize

extern "C" ISocketProvider * CreateTCPSocketProvider();
extern "C" ISocketProvider * CreateTLSSocketProvider(class ISocketProvider * tcpSocketProvider);
extern "C" ISocketProvider * CreateUDPSocketProvider();
extern "C" ISocketProvider * CreateLocalSocketProvider();

Overview

These functions will create an ISockerProvider instance. What kind of socket provider they will return, depends on the function called. As the name tells, the TCP version is for a TCP socket provider, the TLS function for a TLS socket, UDP for a UDP socket and finally LocalSocket will create a socket provider for local sockets. The last one will be used for inter process communication between the apps.
A type of a created socket cannot be changed, except for TCP sockets which can be upgraded to a TLS socket.

CreateTCPSocketProvider
CreateTLSSocketProvider
CreateUDPSocketProvider
CreateLocalSocketProvider

Parameters (CreateTLSSocketProvider only)

class ISocketProvider * tcpSocketProviderA socket provider instance to create a TCP socket used by the TLS socket object..

Return value

The ISocketProvider instance. That instance can be freed as soon as it no longer is used by calling the C++ delete operator.

Remarks

(For CreateTLSSocketProvider only): If a TLS socket provider only will be used to upgrade a TCP sockets, the tcpSocketProvider parameter cann be nullptr.

Classes

ISocketProvider

class ISocketProvider {
public:
    virtual ~ISocketProvider() {};
    virtual ISocketContext * CreateSocketContext(class IInstanceLog * const log);
    virtual ISocket * CreateSocket(class IIoMux * const iomux, USocket * const user, class IInstanceLog * const log, bool useIPv6, class ISocketContext * socketContext = nullptr);
    virtual ISocket * UpgradeToTLSSocket(class ISocket * fromTCPSocket, IIoMux * const iomux, USocket * const user, IInstanceLog * const log, bool initServerSide = false, class ISocketContext * socketContext = nullptr);
};

Overview

The socket provider is used to create an instance of ISocket or ISocketContext. While the ISocketContext is the same for each type of socket provider, the type of ISocket created depends on the type of the ISocketProvider itself. See above for more informations.

Public functions

CreateSocketContext
Creates an ISocketContext instance. ISocketContext will be used to give additional informations to an ISocket. So it must be setup correctly after creation and before passing to create an ISocket instance.

Parameters

class IInstanceLog * const logThe IInstanceLog instance used for loging purposes.

Return value

The ISocketContext instance that can be used to pass it to ISocketProvider::CreateSocket(). It must be freed by the C++ operator delete, if no longer be used.

Remarks

Note that ISocket depends on an assigned ISocketContext. So an assigned ISocketContext must not be deleted before deleting the owing ISocket instance!
CreateSocket
Creates an instance of ISocket. The type of the socket (e. G. TCP, TLS, ...) depends on the type of the ISocketProvider and thus on the Create*SocketProvider() function called.

Parameters

class IIoMux * const iomuxThe IIoMux instance the socket will be registered to.
class USocket * const userThe USocket instance that will be receive the callbacks of ISocket.
class IInstanceLog * const logThe IInstanceLog instance used for loging purposes.
bool useIPv6If set to true, the ISocket will be prepared for IPv6 usage instead of the default IPv4.
class ISocketContext * const socketContext(Default: nullptr) The ISocketContext instance to pass additional information to the socket.

Return value

The ISocket instance to use for socket communication. If no longer used, the instance need to be freed by calling the C++ delete operator.
UpgradeToTSLSocket
Takes the given TCP socket instance and upgrades it to an TLS socket. Depending on the value of initServerSide, the returned ISocket will directly start the TLS handshake or wait for the handshake to be started. As soon as the handshake had been completed, a callback to the given USocket instance will inform the application.

Parameters

class ISocket * fromTCPSocketThe TCPSocket to upgrade. The socket must already be connected successfully.
class IIoMux * const iomuxThe IIoMux instance the socket will be registered to.
class USocket * const userThe USocket instance that will be receive the callbacks of the upgraded ISocket.
class IInstanceLog * const logThe IInstanceLog instance used for loging purposes.
bool initServerSide(Default: false) If ture, the TSL socket will wailt for the other side to start the handshake, else the handshake will be initiated by the TLS socket itself.
class ISocketContext * const socketContext(Default: nullptr) The ISocketContext instance to pass additional information to the socket.

Return value

The ISocket instance to use for socket communication. If no longer used, the instance need to be freed by calling the C++ delete operator.

Callbacks

After everything had been set up and the handshake had been completed, SocketUpgradeToTLSComplete() of the given USocket instance will be called.

Remarks

The returned ISocket object takes resposibility for the given TCP socket. That means, the user for the TCP socket will be changed and it also will be deleted, as soon as the application deletes the returned ISocket object. The best idea is, that an application will completly forget about the TCP socket object passed to UpgradeToTLSSocket().
Passing a TLS socket object to fromTCPSocket will raise an exception. Only pass TCP socket objects. However, even if not recommented, in theory it also will work with UD sockets, as long as the other side also knows how to talk TLS.

ISocketContext

class ISocketContext {
public:
    virtual ~ISocketContext() {};
    virtual void EnableDTLS(bool useSrtp);
    virtual void SetServerCertificate(const byte * cert, size_t certLen, const char * hostName = nullptr);
    virtual void SetClientCertificate(const byte * cert, size_t certLen, const char * hostName = nullptr);
};

Overview

An ISocketContext can be used to handle certificates and other socket related configuration. Because that configuration will be necessary during the creation if ISocket, an ISocketContext instance must be setup properly before passing it to ISocketProvider::CreateSocket()
After the creation of an ISocket, the ISocketContext instance is connected with the ISocket instance, so it must not be deleted until the ISocket instance had beend deleted first.

Remarks

An ISocketContext insatance can be shared with multiple ISockets (which must be taken care of when deleting ISocketContext). Because of the types of informations hold, it currently can only be used for TLS sockets.

Public functions

EnableDTLS
Enables DTLS encryption. Srtp = true --> Benutzt DTLS für Srtp

Parameters

bool useSrtpIf true, the DTLS class will be prepared to be used for secure real-time transport protocol (e. g. for video)
SetServerCertificate
Defines the certificate to use for ISocket server functionality.

Parameters

const byte * certThe buffer to the certificate data.
size_t certLenThe length of the certificate.
const char * hostNameReserved for later use - see remarks.

Remarks

Currently, the hostName functionality is not implemented, so you just override the default server certificate.
Please not that if you set a new server certificate, all currently established conntections will still use the old certificate. The new set certificate only will be used by new connections.
SetClientCertificate
Defines the certificate to use for ISocket client functionality.

Parameters

const byte * certThe buffer to the certificate data.
size_t certLenThe length of the certificate.
const char * hostNameHey - dde - still not used here, too?=

ISocket

class ISocket {
public:
    virtual ~ISocket() {};
    virtual void Connect(const char * address, const char * serverName = NULL);
    virtual void Bind(const char * localAddr = nullptr, word localPort = 0);
    virtual void Listen();
    virtual void Accept(class USocket * remoteUser);
    virtual void Shutdown();
    virtual void Send(const void * buf, size_t len);
    virtual void SendTo(const void * buf, size_t len, struct sockaddr_storage * dstAddr) {};
    virtual void Recv(void * buf, size_t len, bool recvPartial = false);
    virtual void SetUser(class USocket * const user);
};

Overview

The ISocket interface is used for socket communication. After the creation of an ISocket by using an ISocketProvider instance, the usage of the ISocket instance starts by calling Connect() or Bind(). All of the functions will lead to an asynchronous callback of the USocket user instance, passed to ISocketProvider::CreateSocket().

Logging

To enable logging for ISocket, the flag LOG_TCP (for TCP sockets), LOG_TLS (for TLS sockets), LOG_UDP (for UDP sockets) or LOG_LDS (for local sockets) must be set in the managers diagnostic settings.

Remarks

An ISocket instance can be used for either server or socket side of the connection, depending what function had been called. After calling Connect(), the ISocket instance will be in client mode, while a call to Bind() will enable the server mode. Calling a function of the other mode (e. g. calling Listen() or Bind() after calling Connect() and setting the ISocket instance to client mode) will lead to an assert.

Public functions

Bind (server mode)
This function enables the server mode of ISocket and binds it to the given address. After receiving the USocket::BindResult() callback, Listen() must be called to receive incomming connection requests from clients.

Parameters

const char * localAddr(Default nullptr) A String that holds an IPv4 or IPv6 address (for TCP and TLS sockets, also depending on the useIPv6 flag passed for creation)
or address name (for local sockets, normaly a file name). If nullptr is given, the operating system bind the socket to all interfaces available (TCP, TLS and UDP socket only)
word localPort(Default 0) The port the socket should listen to after binding. (TCP and TLS sockets only). If 0 is passed, the operating system will pick a free port.

Callbacks

On success, USocket::BindResult() will be called.
If an error occurres, USocket::SocketShutdown() will be called. Details of that error can be found inside the log, if logging for that type of socket is activated. Reason can be one of the following values:
SOCKET_ADDRESS_INVALIDThe format of the given address is invalid.
SOCKET_OPEN_FAILEDOpening the socket failed because of an error. See log for details.
SOCKET_BIND_FAILEDAn error occured during the bind process. See the log for details.

Remarks

After calling Bind(), a call to Connect(), Send() or Recv() will end up with an assert.
Only for local sockets under Linux: The rights of file given to use for bind will be set to read/write for each users, otherwise other appas can not access the socket because of the apps sandboxing.
Listen (server mode)
After a successfull bind, Listen() must be called to start listening to the address / port combination of Bind(). Only after calling Listen() the ISocket instance will receive and handle connect requests.

Callbacks

If an error occurres, USocket::SocketShutdown() will be called with SOCKET_LISTEN_FAILED as reason. Details of that error can be found inside the log, if logging for that type of socket is activated.
On success, USocket will only be informed about an incoming connect request. In that case, USocket::ListenResult() will be called.

Remarks

Listen only must be called once. But it will lead to couple of USocket::ListenResult() callbacks for each incomming connection request.
Accept (server mode)
Need to be called to accept an incomming connection request. This will accept the incomming request, create internally an ISocket and passes it to USocket::AcceptComplete(). So the USocket instance passed to Accept() is responsible to release the created ISocket instance when no longer needed. See USocket::AcceptComplete() for details.
To deniy the request, nullptr need to be passed to Accept().

Remarks

Note that Accept() must be called from inside USocket::ListenResult()!

Parameters

class USocket * remoteUser The user for the new ISocket, that will be created when accepting the request. Accept() will call AcceptComplete() of the given user. If remoteUser is nullptr, the request will be denied.

Callbacks

If an error occurres, USocket::SocketShutdown() will be called with SOCKET_ACCEPT_FAILED as reason. Details of that error can be found inside the log, if logging for that type of socket is activated.
On success, USocket::AcceptComplete() will be called with the new ISocket instance of the connection as parameter. Note that the USocket instance given to Accept() is responsible for releasing the inside Accept() created ISocket instance.
Connect (client mode)
This function enables the client mode of the ISocket instance and starts to establish a connection to the given address. When the connection had been established, the assigned USocket instance will be informed.

Remarks

Note that after calling Connect(), a call to Bind(), Listen() and Accept() will lead to an assert.

Parameters

const char * address The address to connect to. It must be an IPv4 or IPv6 address as string. An optional port can be given seperated by colon (e. G. "192.168.1.100:4242").
For IPv6 addresses, an the adress itself need to be put inside '[' ']' if a port need to be passed (e. G. [::ffff:c0a8:164]:80)
const char * serverName = NULL The server name to connect to, e.g. 'www.testserver.com'. The server name is used in the server_name extension of TLS connections inside the ClientHello.

Callbacks

On success, ISocket will call USocket::SocketConnectComplete().
If an error occurres, USocket::SocketShutdown() will be called. Details of that error can be found inside the log, if logging for that type of socket is activated. Reason can be one of the following values:
SOCKET_ADDRESS_INVALIDThe format of the given address is invalid.
SOCKET_OPEN_FAILEDOpening the socket failed because of an error. See log for details.
SOCKET_CONNECT_FAILEDAn error occured during the connect process. See the log for details.
SOCKET_CONNECT_REJECTEDThe connection request had been rejected by the host.
SetUser
Sets the given user as active one to receive callbacks, replacing the one passed during socket creation.

Parameters

USocket * const userThe user to set, must not be nullptr.

Remarks

ISocket will not check, if the given user is nullptr or not. However, the developer also should take about not passing an object that already had been deleted.
Send (client mode)
Used to send data to the connected server. Send() can be called directly after calling Connect() and bevore receiving the SocketConnectComplete() callback. In that case, the data to send will be cached. But it is highly recommended to start sending data after the connection had been established as well as send the next part, after receiving the SendComplete() callback. Doing this helps a lot to have a healthy flow control.

Parameters

const void * bufThe buffer with the data to send. Must not be nullptr!
size_t lenThe number of bytes to send. That means, that the size of the buffer should be at least the same as the value passed to len.

Callbacks

On success, USocket::SocketSendResult() will be called when, ISocket has sent the data.
The only case when Send() failes is, when the socket is closing, so there is no special send related error here.
SendTo (client mode)
Used to send data to a given address for an UDP socket. Like Send(), the data will be cached if it not can be send immidiatelly.

Parameters

const void * bufThe buffer with the data to send. Must not be nullptr!
size_t lenThe number of bytes to send. That means, that the size of the buffer should be at least the same as the value passed to len.
sockaddr:storage dstAddrDeprecated, will be replaced with an interface-only solution.

Callbacks

On success, USocket::SocketSendResult() will be called when, ISocket has sent the data.
The only case when Send() failes is, when the socket is closing, so there is no special send related error here.

Remarks

SendTo() is only available for UDP sockets. Every other socket will just ignore a call to of that function.
Recv (client mode)
Tells the ISocket instance to receive data. The SocketRecvResult() callback will be called, as soon as data had been read from the socket.

Parameters

const void * bufThe buffer to write the received data to. Must not be nullptr!
size_t lenThe number of bytes to receive from the socket. The SocketRecvResult() will only be called, if the given amount of data had been read (except if recvPartitial is set to true). The given buffer must be at least big enough to store len bytes to it.
bool recvPartitial(Default false)If set to true, ISocket will call SocketRecvResult() as soon as posibile. That means, that everything between 1 and len bytes had been read.

Callbacks

On success, USocket::SocketRecvResult() with the buffer given to Recv() and the number of bytes read. The only case when Recv() failes is, when the socket is closing, so there is no special send related error here.

Remarks

Warning: If using the default non partitial Recv() (recvPartitial set to false), it must be guaranteed, that there are at least len number of bytes available to read from the socket. If not, the ISocket instance will never call USocket::SocketRecvResult(), because it will wait for the given amount of data to arrive.
Shutdown
Closes the socket. For an ISocket in client mode, all cached data will be send first, because the socket will be closed. However, new calls to Send() or Recv() after calling Shutdown() will be ignored.

Callbacks

After the socket had been closed, USocket::SocketShutdown() will be called with reason SOCKET_SHUTDOWN_NORMAL.

USocket

class USocket {
public:
    virtual ~USocket() {}
    virtual void SocketConnectComplete(ISocket * const socket) {};
    virtual void SocketBindResult(ISocket * const socket, const char * localAddr, word localPort) {};
    virtual void SocketListenResult(ISocket * const socket, const char * remoteAddr, word remotePort) {};
    virtual void SocketAcceptComplete(ISocket * const socket) {};
    virtual void SocketShutdown(ISocket * const socket, shutdownreason_t reason);
    virtual void SocketSendResult(ISocket * const socket) {};
    virtual void SocketRecvResult(ISocket * const socket, void * buf, size_t len) {};
    virtual void SocketRecvFromResult(ISocket * const socket, void * buf, size_t len, struct sockaddr_storage * dstAddr) {};
    virtual void SocketRecvCanceled(ISocket * const socket, void * buf);
    virtual void SocketUpgradeToTLSComplete(ISocket * const socket) {}
};

Overview

USocket class is used to receive callbacks from an ISocket instance. So an app need to subclass USocket and pass an instance of that subclass to ISocketProvider::CreateSocket(). That subclass also has to override each callback that is needed for what the ISocket is going to used. One USocket instance can be given to multiple ISocket instanced. Because of that, the ISocket instance that calls a USocket callback will always be passed as parameter.

Remarks

Except SocketShutdown(), which must be implemented, all other functions have a default implementation. So the developer needs to take care to realy override the necessary functions.

Public functions

SocketConnectComplete
Will be called after an ISocket instance sucessfully established a connection to the given address.

Parameters

ISocket * const socketThe calling ISocket instance.
SocketBindResult
Will be called after an ISochet instance had been successfully bound to an address. Remember that ISocket::Listen() still must be called, before the socket can receive connection requests. So SocketBindResult() would be the right place to do so.

Parameters

ISocket * const socketThe calling ISocket instance.
const char * localAddrThe address the socket had been bound to. Normaly the same value as passed to ISocket::Bind().
word localPortThe port the socket had been bound to. Normaly the same as passed to ISocket::Bind() or, in case that 0 had been passed, the port the OS had been selected.

Remarks

If ISocket::Bind() had been called with nullptr as address (to bind the socket to all available interfaces), localAddr will be "0.0.0.0".
For local sockets, localPort always will be 0.
SocketListenResult
After calling ISocket::Listen(), each incoming connection request to the socket will lead to a USocket::SocketListenResult(). There the application must decide, whether the connection will be accepted or denied. This will be done by calling ISocket::Accept() with an USocket instance (to accept) or with nullptr (to deny).

Parameters

ISocket * const socketThe ISocket instance that received an incomming connection request.
const char * remoteAddressThe address of the client that whants to connect to the socket.
word remotePortThe port of the client that wants to connect to the socket.

Remarks

ISocket::Accept must be called inside USocket::SocketListenResult().
SocketAcceptComplete
Will be called after USocket::ListenResult() calls ISocket::Accept() with an USocket instance. So ISocket will call USocket::SocketAcceptComplete() of the instance, passed to ISocket::Accept() inside USocket::SocketListenResult().

Parameters

ISocket * const socketThe calling ISocket instance. Unlike the other callback functions, the ISocket instance passed here is a new created one that now had been connected to the client, that requested the connection. So USocket is now responsible for this ISocket instance.

Remarks

It is important to understand, that USocket::SocketListenResult() will be called from an ISocket instance, that acts in server mode. But the ISocket instance passed to USocket::SocektAcceptComplete() is a new created instance that acts like a client mode ISocket. So ISocket::Send() and ISocket::Recv() can be called, but not Connect() (because it already is connected). See the documentation of ISocket for more informations.
The USocket instance that received this callback is responsible for the ISocket instance passed to the callback function. So it should be stored in a local variable and freed if no longer needed.
SocketShutdown
Will be called after the socket had been closed. It is guaranteed, that all cached data had been send and no more receiving will be done. Also no other callback will be called any more. So the ISocket instance passed can be safely deleted here.

Parameters

ISocket * const socketThe calling ISocket instance. Because the socket is closed, the given instance can be deleted here.
shutdownreason_t reasonThe reason why the socket had been closed. See data type shutdownreason_t for more informations.
SocketSendResult
Will be called after ISocket::Send() had sent the given data or after the data had been cached and sent after that. Even if ISocket::Send() can send the data directly, it is guaranteed that USocket::SocketSendResult() will never ever be called before returning from ISocket::Send(). It will be a good idea to use USocket::SocketSendResult() to send the next data package. Doing this gives a nice flow controll to the application.

Parameters

ISocket * const socketThe calling ISocket instance.
SocketRecvResult
Will be called, after the application calls ISocket::Recv() and after data had been received.

Parameters

ISocket * const socketThe calling ISocket instance.
void * bufThe buffer that holds the received data. It is the same as passed to ISocket::Recv().
size_t lenThe number of bytes read. If ISocket::Recv() is called with recvPartitial true, len can be anything between 1 and the number of bytes passed to ISocket::Recv(). If ISocket::Recv() is called with recvPartitial fase, it is guaranteed, that len is the same as passed to ISocket::Recv() before.
SocketRecvFromResult
The same as Recv(), with the difference that the origin of the data will be passed, too. Only for UDP sockets.

Parameters

ISocket * const socketThe calling ISocket instance.
void * bufThe buffer that holds the received data. It is the same as passed to ISocket::Recv().
size_t lenThe number of bytes read. If ISocket::Recv() is called with recvPartitial true, len can be anything between 1 and the number of bytes passed to ISocket::Recv(). If ISocket::Recv() is called with recvPartitial fase, it is guaranteed, that len is the same as passed to ISocket::Recv() before.
sockaddr_storage * dstAddrDepracated, will be replaced by an interface internaly data type

Remarks

This callback will only be called from UDP ISocket instances.
SocketRecvCanceled
Will be called after calling ISocket::RecvCancel() and after canceling had been done. An application must implement that callback to free the buffer passed to Recv() (if not freed elsewhere)

Parameters

ISocket * const socket The calling ISocket instance.
void * buf The pointer to the buffer passed to the last Recv() call.
SocketUpgradeToTLSComplete
When calling ISocketProvider::UpgradeToTSLSocket() to upgrade an TCP socket for using TLS (e. g. for StartTLS), this function will be called after everything is set up and the TLS handshake had been successfully completed. From now on, the connection communicates in an encrypted way.

Parameters

ISocket * const socket The calling ISocket instance. Unlike the other callback functions, the ISocket instance passed here is a new created one and the same as returned by ISocketProvider::UpgradeToTLSSocket().

Data types

shutdownreason_t

Overview

The data type shutdownreason_t will be passed to USocket::SocketShutdown() to descr ibe the reason, why the socket had been closed.

Values

SOCKET_SHUTDOWN_NORMALThe socket had been closed because of a call to ISocket::Shutdown().
SOCKET_SHUTDOWN_BY_PEERThe socket had been closed by the other side of the connection.
SOCKET_LOSTthe connection got lost. This could have various reasons: the other side crashed, the physical connection got lost, ...
SOCKET_ADDRESS_INVALIDThe given address for ISocket::Connect() or ISocket::Bind() is invalid.
SOCKET_OPEN_FAILEDOpening the socket failed because of an error. See log for details.
SOCKET_CONNECT_FAILEDAn error occured during the connect process. See the log for details.
SOCKET_CONNECT_REJECTEDThe connection request had been rejected by the host.
SOCKET_BIND_FAILEDAn error occured during the bind process. See the log for details.
SOCKET_LISTEN_FAILEDA call to ISocket::Listen() failed because of an error. See the log for details.
SOCKET_ACCEPT_FAILEDA call to ISocket::Accept() had been failed because of an error. See tje log for details.
SOCKET_TLS_HANDSHAKE_FAILED(Only TLS sockets) The TLS handshake failed because of an error. See the log for details.

Code Example

ISocket - server

// App with ISocket in server mode
app::app(class IIoMux * iomux)
    : iomux(iomux)
{
    ISocketProvider * provider = CreateTCPSocketProvider();
    this->serverSocket = provider->Create(iomux, this, false);
    delete provider;

    this->serverSocket->Bind();
}

void app::SocketBindResult(ISocket * const socket, const char * localAddr, word localPort)
{
    this->serverSocket->Listen();
}

void app::SocketListenResult(ISocket * const socket, const char * remoteAddr, word remotePort)
{
    // Deny local connections...
    if (strcmp(remoteAddr, "127.0.0.1") == 0)
        this->serverSocket->Accept(nullptr);
    else
        this->serverSocket->Accept(new remoteUser());
}

void app::SocketShutdown(ISocket * const socket, shutdownreason_t reason)
{
    // If true, an error occured during a call to Bind() and SocketBindResult()
    if (this->serverSocket == nullptr)
        delete socket;
    else {
        delete this->serverSocket;
        this->serverSocket = nullptr;
    }
    this->iomux->Terminate();
}

ISocket - client

// remoteUser is subclassed from USocket
void remoteUser::SocketAcceptComplete(ISocket * const socket)
{
    this->remoteSocket = socket;
    this->remoteSocket->Recv(buffer, 1024);
    this->remoteSocket->Send("Hello world", 11);
}

void remoteUser::SocketSendResult(ISocket * const socket)
{
    printf("Data had been sent\n");
}

void remoteUser::SocketRecvResult(ISocket * const socket, void * buf, size_t len)
{
    printf("Received data\n");
    debug->HexDump(buf, len);
    this->remoteSocket->Recv(buffer, 1024);
}

void remoteUser::SocketShutdown(ISocket * const socket, shutdownreason_t reason)
{
    delete socket;
    delete this;
}