NanoSDK C Client Library

repository·main·Indexed 41 days ago

https://github.com/emqx/nanosdk

A high-performance, asynchronous C client library for MQTT (3.1.1/5.0) and QUIC, built on the NNG (nanomsg-NNG) framework. It provides scalable and portable MQTT communication for IoT applications, featuring support for TCP, TLS, and QUIC transports, as well as SQLite-backed persistence for message retries.

Tokens
161.9K
Snippets
585
Records
789
Agent score
51%

What's inside NanoSDK

  1. Overview of NanoSDK C Client Library

    main

    NanoSDK is an open-source C client library for the MQTT protocol, built as a wrapper around the NNG (nanomsg-NNG) framework. It extends NNG by adding significant capabilities for MQTT (3.1.1 and 5.0) and QUIC while maintaining compatibility with original Scalability Protocols (SP).

    Key features include:

    • Fully Asynchronous I/O: Uses an Actor-like programming model in C to distribute computation across multiple CPU cores.
    • High Throughput & Low Latency: Optimized for high-performance MQTT workloads, including asynchronous Ack capability for QoS 1/2 messages to prevent message congestion.
    • Portability: Relies on native POSIX standard APIs, making it highly portable across various Linux distributions and hardware platforms.
    • Flexible API Styles: Supports both the advanced NNG asynchronous/context-based programming model and a traditional callback registration mechanism (similar to Paho or Mosquitto) for easier adoption.
  2. Overview of libnng messaging framework

    main
    libnng (nanomsg next generation) is a C language messaging framework designed to solve common communication problems in distributed applications. It provides a variety of scalability protocols, transport mechanisms, and asynchronous I/O capabilities.
  3. Overview of the mqtt_full CLI demo

    main

    The mqtt_full demo provides a single CLI binary named mqtt_client used to interact with MQTT brokers. It supports three primary subcommands:

    • conn: Connects to a broker and maintains the connection (runs until interrupted by Ctrl+C).
    • sub: Subscribes to topics and prints received messages.
    • pub: Publishes messages to a specific topic.

    Supported URL schemes depend on how the underlying nng library was compiled:

    • mqtt-tcp://... (Standard TCP)
    • tls+mqtt-tcp://... (MQTT over TLS)
    • mqtt-quic://... (MQTT over QUIC)
    mqtt_client <conn|sub|pub> [options]
  4. Overview of the mqtt_client Demo

    main

    The mqtt_full demo provides a command-line executable named mqtt_client used to interact with MQTT brokers. It supports three primary subcommands:

    • conn: Establishes a connection and runs continuously (until interrupted by Ctrl+C).
    • sub: Subscribes to topics and receives messages.
    • pub: Publishes messages to a topic.

    Supported URL schemes:

    • mqtt-tcp://... (Standard TCP)
    • tls+mqtt-tcp://... (MQTT over TLS)
    • mqtt-quic://... (MQTT over QUIC)
    mqtt_client <conn|sub|pub> [options]
  5. Use WebSocket transport for SP

    main

    The WebSocket transport allows using SP (Scalability Protocols) over WebSocket using either TCP or TLS.

    • TCP Compatibility: When using TCP, this transport is compatible with libnanomsg legacy transport and mangos (both TCP and TLS).
    • TLS Requirement: To use TLS support, the mbedTLS library must be available.
    • Message Format: Each SP message is encapsulated as a standard WebSocket message (RFC 6455).

    Protocol Negotiation

    Protocol selection is handled via the Sec-WebSocket-Protocol field in the WebSocket handshake. The client sets this field to the server's protocol. For example, if the server is a REP endpoint, a REQ client should send rep.sp.nanomsg.org. The server then responds with its own protocol value as per WebSocket specifications.

  6. Use the TCP/IP transport in NNG

    main

    The TCP transport provides communication support between sockets across a TCP/IP network, supporting both IPv4 and IPv6. It is generally built into the core of NNG, so no extra registration steps are typically required for standard builds.

    #include <nng/transport/tcp/tcp.h>
    
    int nng_tcp_register(void);
  7. Use nngcat for command line access to Scalability Protocols

    main

    The nngcat utility provides a command-line interface to interact with Scalability Protocols. It allows you to write shell scripts that can send and receive messages by acting as a peer in a protocol topology.

    To use nngcat, you must select at least one Protocol (e.g., --req, --pub, --sub) and at least one Peer Address (via --connect or --bind).

  8. Use the ZeroTier transport in NNG

    main

    The ZeroTier (zt://) transport allows NNG applications to communicate over a ZeroTier network using a Virtual Layer 2 packet facility.

    Important Considerations:

    • Experimental: This transport is experimental.
    • Dependencies: The library must be built with ZeroTier support and linked against libzerotiercore.
    • Connection Latency: Initial connections can take up to a minute to establish while the network topology configures. It is recommended for long-running programs (daemons/agents) rather than short-lived ones.
    • Network Requirements: Network rules must permit Ethernet type 901 to pass.
    • IP Stack: This transport does not require or use an IP stack on the virtual network, mitigating IP management issues.
    #include <nng/transport/zerotier/zerotier.h>
    
    // Register the transport if necessary
    int nng_zt_register(void);
  9. What is an nng_ctx (protocol context)?

    main

    An nng_ctx is a handle to an underlying context object that maintains protocol state for stateful protocols.

    Its primary purpose is to allow applications to share a single socket (and its associated dialers, listeners, and pipes) while maintaining separate state tracking for different operations. For example, in a _req_ (request) protocol, each context can track its own request ID and retry timers independently, even though they all use the same underlying socket.

    Key Characteristics:

    • Opaque Type: The nng_ctx structure is always passed by value and should be treated as an opaque handle.
    • Shared Socket: All contexts on a socket share the same underlying transport details and certain socket options.
    • Concurrency: Contexts are the preferred way to implement concurrency in stateful protocols, serving as a safer alternative to using raw mode sockets.
    • Protocol Support: Not all protocols support contexts. Check the specific protocol documentation for compatibility and supported options.
    • Global Context: Protocols using contexts typically provide a default context for global socket operations.
  10. What is an nng_pipe?

    main

    An nng_pipe is a handle to a pipe object, which represents a single communication connection (typically an abstraction for a single TCP or IPC connection).

    Key characteristics:

    • Association: Pipes are associated with the nng_dialer or nng_listener that created them, and are automatically linked to a single socket.
    • Opaque Handling: The nng_pipe structure must always be passed by value (for both input parameters and return values) and should be treated as an opaque type.
    • Lifecycle: Pipes are created by dialers or listeners. They are destroyed when:
      1. nng_pipe_close() is called.
      2. The dialer or listener that created them is closed.
      3. The remote peer closes the underlying connection.

    While most applications do not need to interact with individual pipes, they can be accessed when specific control over message delivery or source information is required.

  11. What is an nng_aio handle?

    main

    An nng_aio is an opaque structure used to manage asynchronous I/O operations. Every asynchronous operation in the library requires an nng_aio handle.

    Key constraints and behaviors:

    • Single Use: Each handle can only be used with a single operation at a time.
    • Non-blocking: Asynchronous operations are performed without blocking the calling application threads.
    • Callbacks: Instead of waiting for a return value, the application registers a callback function that is executed exactly once when the operation completes (successfully or otherwise).
    • Lifecycle: Handles are created using nng_aio_alloc() and must be destroyed using nng_aio_free() to prevent memory leaks.
    #include <nng/nng.h>
    
    typedef struct nng_aio nng_aio;
  12. What is an NNG context and when to use one

    main

    An NNG context is an abstraction that allows for the independent and concurrent use of stateful operations on a single socket.

    When to use: Use contexts when you need to perform multiple concurrent operations on the same socket (like a rep socket handling multiple requests) without the operations interfering with each other. This allows you to write simpler concurrent logic while still benefiting from the protocol's built-in advanced processing, rather than resorting to raw mode sockets.

    When not to use:

    • Do not use contexts with raw mode sockets; it is unsupported and nonsensical.
    • Be aware that some protocols may not support context creation at all.