Skip to content

TCP and UDP modules

Gautier Lefebvre edited this page Feb 13, 2018 · 7 revisions

TCP and UDP modules

Both TCP and UDP modules allow you to create multiple servers and multiple clients.

They both offer events you can subscribe to. All events from these modules are synchronous.

See the Events wiki for a better understanding of how to subscribe to these events, and how to reinterpret the types of the events' arguments.

Initialize the modules

See the System wiki.

int main(void) {
    fwk::System* framework = new fwk::System();

    // initialize the modules you want to use.
    framework->initTCP();
    framework->initUDP();

    framework->run();
    delete framework;
}

TCP

TCP Server

Bind server

fwk::TcpManager& tcpManager = fwk::NetworkManager::get().getTCP();

// prepare server
uint16_t port = 4444;
const fwk::TcpServer& server = tcpManager.createServer(port);

// bind and listen
tcpManager.run(server);

Events

The reason why you need to create your server, then run it, is to allow you to subscribe to events before they are fired, which would make you miss information.

All the events subscription below are between the createServer and run method calls.

On new client
server.events.onAccept->subscribe(
    [] (const fwk::IEventArgs* ptr) {
        const fwk::TcpSocketStreamEventArgs* args =
            reinterpret_cast<const fwk::TcpSocketStreamEventArgs*>(ptr);

        // the new client socket
        fwk::TcpSocketStream* socket = args->socket;
    },
    this); // key (see Events wiki)
On data received (+ extract data)

This example shows how to subscribe to the onReceivedData event, and how to extract the data received.

Remember that this event, as every other event from this module, is synchronous.

This means that your callback is called directly by the TCP/UDP input thread, so keep operations to a minimum. If you need bigger operations, you can use a fwk::SimpleTask (see the Tasks wiki).

server.events.onReceivedData->subscribe(
    [] (const fwk::IEventArgs* ptr) {
        const fwk::TcpSocketStreamEventArgs* args =
            reinterpret_cast<const fwk::TcpSocketStreamEventArgs*>(ptr);

        // the client who sent the data
        fwk::TcpSocketStream* socket = args->socket;

        // create a ByteArray in which to extract the received data (if any).
        fwk::ByteArray* data = fwk::ByteArray::getFromPool();
        
        // extract the data
        socket->extractData(
            [] (const fwk::ByteArray& input) -> size_t {
                // in this callback, you go through the input ByteArray
                // to find the length of the message you are trying to extract

                // in this example, we extract the complete buffer
                return input.getSize();
            },
            data);

        // the data ByteArray is now filled with the data received on the socket.

        // [Use the data]

        // Return the ByteArray to its pool if you no longer need it
        fwk::ByteArray::returnToPool(data);
    },
    this); // key (see Events wiki)
On client closed
server.events.onClientClosed->subscribe(
    [] (const fwk::IEventArgs* ptr) {
        const fwk::TcpSocketStreamEventArgs* args =
            reinterpret_cast<const fwk::TcpSocketStreamEventArgs*>(ptr);

        // the client socket which closed
        fwk::TcpSocketStream* socket = args->socket;
    },
    this); // key (see Events wiki)
On server closed

The fwk::TcpSocket is the same as the server.server variable.

server.events.onClosed->subscribe(
    [] (const fwk::IEventArgs* ptr) {
        const fwk::TcpSocketEventArgs* args =
            reinterpret_cast<const fwk::TcpSocketEventArgs*>(ptr);

        // the socket which closed
        fwk::TcpSocket* socket = args->socket;
    },
    this); // key (see Events wiki)

TCP Client

Create client

fwk::TcpManager& tcpManager = fwk::NetworkManager::get().getTCP();

// prepare client
std::string hostname = "localhost";
uint16_t port = 4444;
const fwk::TcpClient& client = tcpManager.createClient(hostname, port);

// connect
tcpManager.run(client);

Events

See this.

On data received

Same as server data received.

On socket closed
client.events.onClosed->subscribe(
    [] (const fwk::IEventArgs* ptr) {
        const fwk::TcpSocketStreamEventArgs* args =
            reinterpret_cast<const fwk::TcpSocketStreamEventArgs*>(ptr);

        // the socket which closed
        fwk::TcpSocketStream* socket = args->socket;
    },
    this); // key (see Events wiki)

Send data

From any fwk::TcpSocketStream (gotten from onAccept or when creating a client), you can add data which will be pushed as soon as possible, asynchronously.

You can also send a fwk::ByteArray directly.

const char message[] = "foo";

// see above to get the TcpManager
tcpManager.push(
    socketStream, // fwk::TcpSocketStream*
    message,
    strlen(message));

UDP

You need to remember that UDP is not a reliable protocol. The events onClosed or onClientClosed do not work properly when the "connection" is closed by the other side.

Server

Create server

fwk::UdpManager& udpManager = fwk::NetworkManager::get().getUDP();

// prepare server
uint16_t port = 4444;
const fwk::UdpServer& server = udpManager.createServer(port);

// bind
udpManager.run(server);

Events

Remember that events are synchronous.

On new client
server.events.onNewClient->subscribe(
    [] (const fwk::IEventArgs* ptr) -> void {
        const fwk::UdpSocketClientEventArgs* args =
            reinterpret_cast<const fwk::UdpSocketClientEventArgs*>(ptr);

        // new client
        fwk::UdpSocketClient* socket = args->socket;
    },
    this);
On data received
server.events.onReceivedData->subscribe(
    [] (const fwk::IEventArgs* ptr) -> void {
        const fwk::UdpSocketClientEventArgs* args =
            reinterpret_cast<const fwk::UdpSocketClientEventArgs*>(ptr);

        // client who sent the data
        fwk::UdpSocketClient* socket = args->socket;

        // extract the last received datagram
        ByteArray* datagram = socket->getData();
        if (data != nullptr) {
            // use data
        }
    },
    this);
On client closed
server.events.onClientClosed->subscribe(
    [] (const fwk::IEventArgs* ptr) -> void {
        const fwk::UdpSocketClientEventArgs* args =
            reinterpret_cast<const fwk::UdpSocketClientEventArgs*>(ptr);

        // closed client
        fwk::UdpSocketClient* socket = args->socket;
    },
    this);
On server closed
server.events.onClosed->subscribe(
    [] (const fwk::IEventArgs* ptr) -> void {
        const fwk::UdpSocketServerEventArgs* args =
            reinterpret_cast<const fwk::UdpSocketServerEventArgs*>(ptr);

        // closed client
        fwk::UdpSocketServer* socket = args->socket;
    },
    this);

Client

Create client

fwk::UdpManager& udpManager = fwk::NetworkManager::get().getUDP();

// prepare client
std::string hostname = "localhost";
uint16_t port = 4444;
const fwk::UdpClient& client =
    udpManager.createClient(hostname, port);

// "connect"
udpManager.run(client);

Events

On data received
client.events.onReceivedData->subscribe(
    [] (const fwk::IEventArgs* ptr) -> void {
        const fwk::UdpSocketStreamEventArgs* args =
            reinterpret_cast<const fwk::UdpSocketStreamEventArgs*>(ptr);

        // get the socket
        fwk::UdpSocketStream* socket = args->socket;

        // extract the last received datagram
        ByteArray* datagram = socket->getData();
        if (data != nullptr) {
            // use data
        }
    },
    this);
On socket closed
client.events.onClosed->subscribe(
    [] (const fwk::IEventArgs* ptr) -> void {
        const fwk::UdpSocketStreamEventArgs* args =
            reinterpret_cast<const fwk::UdpSocketStreamEventArgs*>(ptr);

        // closed socket
        fwk::UdpSocketStream* socket = args->socket;
    },
    this);

Send datagram

From any fwk::UdpSocketStream (gotten when creating a client) or fwk::UdpSocketClient (gotten from onNewClient event), you can push datagrams which will be sent as soon as possible, asynchronously.

You can also send a fwk::ByteArray directly.

const char message[] = "foo";

fwk::NetworkManager::get().getUDP().push(
    socket, // fwk::UdpSocketStream* or fwk::UdpSocketClient*
    message,
    strlen(message));