Elegir entre las distintas librerías cliente MQTT es la primera decisión de ingeniería real en cualquier proyecto MQTT, y una mala elección aparece más tarde en forma de errores de reconexión, funciones de MQTT 5 que faltan o un bucle de eventos bloqueado. Esta guía compara las opciones mantenidas para Python, JavaScript, Go, Java, C#, Rust, C, microcontroladores y móvil, y te da para cada una un fragmento mínimo que se conecta al broker gratuito broker.connectmq.com. Al terminar sabrás qué librería encaja con tu stack y qué comprobar antes de comprometerte con ella.
Qué hace realmente una librería cliente MQTT
Una librería cliente MQTT oculta el protocolo binario tras unas pocas llamadas: conectar, suscribirse, publicar, desconectar. Por debajo hace mucho más, y es en esas tareas ocultas donde las librerías se diferencian:
- Codificación y decodificación de paquetes para MQTT 3.1.1 y, en las mejores librerías, las propiedades de MQTT 5.0 como reason codes, caducidad de mensajes y user properties.
- Pings de keep-alive para que el broker sepa que la conexión está sana.
- Máquinas de estado de QoS que guardan los mensajes QoS 1 y QoS 2 en tránsito y los reintentan tras una reconexión.
- Lógica de reconexión con backoff, además de volver a suscribirse cuando la sesión no se ha conservado.
- Gestión del transporte para TCP, TLS y WebSocket.
- Integración con hilos o asincronía de tu runtime: un hilo en segundo plano, un bucle de eventos, goroutines o callbacks.
Una librería que codifica bien el protocolo pero gestiona mal las reconexiones pasará todas las demos y fallará en campo. Tenlo en cuenta al leer la comparativa. Si el lado del broker aún no te queda claro, lee primero qué hace un broker MQTT; todas las librerías de esta guía no son más que clientes de uno.
Cómo evaluar librerías cliente MQTT
Usa esta lista de comprobación antes de adoptar cualquier librería, sea cual sea el lenguaje:
- Mantenimiento. Mira las versiones recientes y cómo se gestionan las incidencias. MQTT en sí es estable, pero las pilas TLS, los runtimes y las plataformas cambian constantemente.
- Versión del protocolo. ¿Necesitas MQTT 5? Las suscripciones compartidas, la caducidad de mensajes, el patrón petición/respuesta y los reason codes detallados solo existen ahí.
- Transportes. Los navegadores necesitan WebSocket; las redes corporativas restringidas a menudo exigen WSS en puertos tipo 443; los dispositivos suelen querer TLS en el 8883.
- Modelo de concurrencia. Basado en callbacks, en hilos o en async/await. Elige lo que encaje con el resto de tu código, no lo que parezca más corto en un README.
- Reconexión y buffer sin conexión. ¿Se reconecta automáticamente? ¿Encola las publicaciones mientras está desconectada, y esa cola tiene límite?
- Persistencia de los mensajes en tránsito. ¿Solo en memoria o en archivo? Esto importa para QoS 1 y 2 cuando el proceso se reinicia.
- Huella. En microcontroladores, la RAM y la flash lo deciden todo; en servidores, rara vez importan.
- Licencia. La mayoría de las librerías populares usan EPL/EDL, MIT o Apache 2.0, pero compruébalo antes de lanzar un producto comercial.
Librerías cliente MQTT de un vistazo
| Lenguaje / plataforma | Librería | MQTT 5 | Modelo | Adecuada para |
|---|---|---|---|---|
| Python | paho-mqtt 2.x | Sí | Hilo en segundo plano o bucle manual | Scripts, gateways, Raspberry Pi |
| Python | aiomqtt | Sí | asyncio | Servicios asíncronos, backends con FastAPI |
| JavaScript / TypeScript | MQTT.js 5.x | Sí | Event emitter, promesas | Node.js y navegadores (WebSocket) |
| Go | Eclipse Paho Go (paho.mqtt.golang) | No (3.1.1) | Tokens, goroutines | Servicios 3.1.1 maduros |
| Go | Eclipse Paho paho.golang | Sí | Basado en context | Nuevos servicios MQTT 5 |
| Java / Kotlin | Eclipse Paho Java (mqttv3, mqttv5) | Sí (paquete v5) | Clientes síncronos y asíncronos | Backends JVM, Android |
| Java / Kotlin | HiveMQ MQTT Client | Sí | Bloqueante, asíncrono, reactivo | Stacks JVM reactivos |
| C# / .NET | MQTTnet | Sí | async/await | ASP.NET Core, MAUI, Unity |
| Rust | rumqttc | Sí | Asíncrono (tokio) o síncrono | Servicios edge, gateways |
| C | Eclipse Paho C, libmosquitto | Sí | Síncrono y asíncrono | Dispositivos Linux, aplicaciones nativas |
| Arduino / ESP32 | PubSubClient, AsyncMqttClient | No (3.1.1) | Basado en loop / callbacks asíncronos | Microcontroladores |
| ESP-IDF | esp-mqtt | Sí | Manejador de eventos | Firmware ESP32 de producción |
| Swift | CocoaMQTT | Sí | Delegate / closures | Apps de iOS y macOS |
| Dart / Flutter | mqtt_client | No (3.1.1) | Streams | Móvil multiplataforma |
La tabla refleja las capacidades documentadas de las librerías en el momento de escribir este artículo. Confírmalo siempre con las notas de la versión actual de cada proyecto, porque el soporte de MQTT 5 en particular ha ido llegando por etapas. La página del proyecto Eclipse Paho en eclipse.dev/paho es un buen punto de partida para la familia Paho.
Python: paho-mqtt y aiomqtt
paho-mqtt es la opción por defecto en Python y funciona casi en cualquier sitio, desde una Raspberry Pi hasta un contenedor. La versión 2.x introdujo un cambio incompatible: debes indicar una versión de la API de callbacks, y los callbacks v2 reciben reason codes y propiedades. La mayoría de los informes de "no funciona" vienen de copiar ejemplos de la 1.x en una instalación 2.x, algo que explicamos en detalle en la solución de CallbackAPIVersion en paho-mqtt 2.0.
import uuid
import paho.mqtt.client as mqtt
TOPIC = "connectmq/demo/k3x8p1/libs/python"
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code)
client.subscribe(TOPIC, qos=1)
client.publish(TOPIC, "hello from paho-mqtt", qos=1)
def on_message(client, userdata, msg):
print(msg.topic, msg.payload.decode())
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"libs-py-{uuid.uuid4().hex[:8]}",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_message = on_message
client.tls_set()
client.connect("broker.connectmq.com", 8883, keepalive=30)
client.loop_forever()
aiomqtt envuelve paho en una API asyncio idiomática con async with y async for. Si tu servicio ya ejecuta un bucle de eventos, úsala en lugar de mezclar hilos y corrutinas. Los recorridos completos están en nuestro tutorial de paho-mqtt en Python y en la guía de aiomqtt con asyncio.
JavaScript y TypeScript: MQTT.js
MQTT.js es la librería de facto para Node.js y el navegador, incluye tipos de TypeScript y admite MQTT 5. En Node.js se conecta por TCP o TLS; en el navegador usa WebSocket, así que lo único que cambia es el esquema de la URL.
import mqtt from "mqtt";
const clientId = "libs-js-" + Math.random().toString(16).slice(2, 10);
const topic = "connectmq/demo/k3x8p1/libs/javascript";
// Node.js: mqtts://broker.connectmq.com:8883
// Navegador: wss://broker.connectmq.com:8084/mqtt
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId,
protocolVersion: 5,
});
client.on("connect", async () => {
await client.subscribeAsync(topic, { qos: 1 });
await client.publishAsync(topic, "hello from MQTT.js", { qos: 1 });
});
client.on("message", (t, payload) => console.log(t, payload.toString()));
client.on("error", (err) => console.error(err.message));
MQTT.js se reconecta automáticamente por defecto (reconnectPeriod controla el retardo). En el navegador, recuerda que una página HTTPS solo puede abrir conexiones wss://. Los patrones específicos de cada framework están en MQTT.js con Node.js y MQTT en el navegador con JavaScript.
Go: Eclipse Paho
Go tiene dos proyectos de Eclipse. paho.mqtt.golang es el veterano cliente MQTT 3.1.1 que usan muchísimos servicios en producción. paho.golang es la implementación más reciente de MQTT 5, con un paquete autopaho que gestiona la reconexión por ti. Para un proyecto nuevo con MQTT 5, empieza con paho.golang; para una compatibilidad amplia con 3.1.1 y el mayor volumen de ejemplos, el cliente clásico sigue siendo una opción sólida.
package main
import (
"crypto/tls"
"fmt"
"math/rand"
"time"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
func main() {
topic := "connectmq/demo/k3x8p1/libs/go"
opts := mqtt.NewClientOptions().
AddBroker("tls://broker.connectmq.com:8883").
SetClientID(fmt.Sprintf("libs-go-%06d", rand.Intn(1000000))).
SetTLSConfig(&tls.Config{MinVersion: tls.VersionTLS12}).
SetAutoReconnect(true)
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
client.Subscribe(topic, 1, func(_ mqtt.Client, m mqtt.Message) {
fmt.Printf("%s %s\n", m.Topic(), m.Payload())
}).Wait()
client.Publish(topic, 1, false, "hello from Go").Wait()
time.Sleep(2 * time.Second)
client.Disconnect(250)
}
Consulta cómo crear un cliente MQTT en Go para el apagado ordenado y los payloads estructurados.
Java y Kotlin: Paho y HiveMQ Client
Eclipse Paho Java incluye dos paquetes: org.eclipse.paho.client.mqttv3 para MQTT 3.1.1 y org.eclipse.paho.mqttv5.client para MQTT 5. HiveMQ MQTT Client es una alternativa con APIs bloqueantes, asíncronas y reactivas que encaja en bases de código con Reactor o RxJava. Ambos funcionan también en Android, por eso las apps en Kotlin suelen usarlos directamente.
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import java.util.UUID;
public class LibsJava {
public static void main(String[] args) throws Exception {
String topic = "connectmq/demo/k3x8p1/libs/java";
String clientId = "libs-java-" + UUID.randomUUID().toString().substring(0, 8);
MqttClient client = new MqttClient("ssl://broker.connectmq.com:8883",
clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(true);
options.setCleanSession(true);
client.connect(options);
client.subscribe(topic, 1, (t, msg) ->
System.out.println(t + " " + new String(msg.getPayload())));
client.publish(topic, "hello from Paho Java".getBytes(), 1, false);
Thread.sleep(2000);
client.disconnect();
client.close();
}
}
Quienes usan Spring Boot suelen envolver esto en Spring Integration; los patrones están en nuestra guía de clientes MQTT en Java y en el tutorial de MQTT en Android con Kotlin.
C# y .NET: MQTTnet
MQTTnet es la librería estándar de .NET y admite MQTT 5, TLS y WebSocket. Es totalmente asíncrona y encaja en servicios hospedados de ASP.NET Core, apps de .NET MAUI y Unity. Ten en cuenta que las versiones principales difieren: la versión 5 renombró la factoría a MqttClientFactory, mientras que la versión 4 usa MqttFactory. El fragmento está escrito para la versión 5.
using MQTTnet;
using MQTTnet.Formatter;
var topic = "connectmq/demo/k3x8p1/libs/csharp";
var factory = new MqttClientFactory();
using var client = factory.CreateMqttClient();
client.ApplicationMessageReceivedAsync += e =>
{
Console.WriteLine($"{e.ApplicationMessage.Topic} {e.ApplicationMessage.ConvertPayloadToString()}");
return Task.CompletedTask;
};
var options = new MqttClientOptionsBuilder()
.WithTcpServer("broker.connectmq.com", 8883)
.WithClientId($"libs-cs-{Guid.NewGuid():N}"[..16])
.WithProtocolVersion(MqttProtocolVersion.V500)
.WithTlsOptions(o => o.UseTls())
.Build();
await client.ConnectAsync(options);
await client.SubscribeAsync(factory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(topic).Build());
await client.PublishAsync(new MqttApplicationMessageBuilder()
.WithTopic(topic).WithPayload("hello from MQTTnet").Build());
await Task.Delay(2000);
await client.DisconnectAsync();
El cliente de bajo nivel de MQTTnet no se reconecta por sí solo, así que envuelve la conexión en un bucle o en un servicio en segundo plano. El tutorial de MQTTnet muestra un patrón de servicio hospedado resiliente.
Rust: rumqttc
rumqttc es el cliente de Rust más utilizado. Separa el handle del cliente de un bucle de eventos que tú sondeas, lo que te da un control explícito sobre la contrapresión. Existen módulos tanto para MQTT 3.1.1 como para MQTT 5.
use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS};
use std::time::Duration;
#[tokio::main]
async fn main() {
let topic = "connectmq/demo/k3x8p1/libs/rust";
let mut opts = MqttOptions::new("libs-rust-k3x8p1", "broker.connectmq.com", 1883);
opts.set_keep_alive(Duration::from_secs(30));
let (client, mut eventloop) = AsyncClient::new(opts, 10);
client.subscribe(topic, QoS::AtLeastOnce).await.unwrap();
client.publish(topic, QoS::AtLeastOnce, false, "hello from rumqttc").await.unwrap();
loop {
match eventloop.poll().await {
Ok(Event::Incoming(Packet::Publish(p))) => {
println!("{} {}", p.topic, String::from_utf8_lossy(&p.payload));
}
Ok(_) => {}
Err(e) => {
eprintln!("connection error: {e}");
tokio::time::sleep(Duration::from_secs(2)).await;
}
}
}
}
Volver a sondear el bucle de eventos tras un error es la forma en que rumqttc se reconecta. TLS requiere activar uno de sus feature flags de TLS; la guía de rumqttc explica esa configuración.
C y sistemas embebidos: Paho C, libmosquitto, PubSubClient, esp-mqtt
Para aplicaciones nativas en Linux, Eclipse Paho C ofrece APIs síncronas y asíncronas con soporte de MQTT 5, y libmosquitto es una alternativa pequeña y muy probada del proyecto Mosquitto.
#include <stdio.h>
#include <string.h>
#include "MQTTClient.h"
int main(void) {
MQTTClient client;
MQTTClient_connectOptions opts = MQTTClient_connectOptions_initializer;
MQTTClient_message msg = MQTTClient_message_initializer;
MQTTClient_deliveryToken token;
const char *topic = "connectmq/demo/k3x8p1/libs/c";
char payload[] = "hello from Paho C";
MQTTClient_create(&client, "tcp://broker.connectmq.com:1883", "libs-c-k3x8p1",
MQTTCLIENT_PERSISTENCE_NONE, NULL);
opts.keepAliveInterval = 30;
opts.cleansession = 1;
if (MQTTClient_connect(client, &opts) != MQTTCLIENT_SUCCESS) {
fprintf(stderr, "connect failed\n");
return 1;
}
msg.payload = payload;
msg.payloadlen = (int)strlen(payload);
msg.qos = 1;
msg.retained = 0;
MQTTClient_publishMessage(client, topic, &msg, &token);
MQTTClient_waitForCompletion(client, token, 10000L);
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return 0;
}
En microcontroladores, el equilibrio cambia. PubSubClient es diminuta y sencilla, pero solo admite MQTT 3.1.1, publica con QoS 0 y tiene un buffer de paquetes por defecto de 256 bytes que debes ampliar con setBufferSize() para payloads JSON. AsyncMqttClient añade publicación con QoS 1 y 2 con un diseño no bloqueante. Para firmware ESP32 de producción, el componente esp-mqtt de Espressif en ESP-IDF admite MQTT 5, TLS y WebSocket y lo mantiene el propio fabricante del chip. Empieza con el tutorial ESP32 MQTT para tener código Arduino funcional, y con Paho MQTT C para dispositivos Linux.
Móvil: Swift, Kotlin, Flutter y React Native
Los clientes móviles se enfrentan a una restricción que los servidores no tienen: el sistema operativo suspende las apps en segundo plano y cierra los sockets inactivos. Elijas la librería que elijas, prevé reconexiones frecuentes, usa sesiones persistentes o retained messages para ponerte al día y recurre a notificaciones push para todo lo que deba llegar a un teléfono en reposo.
- iOS / macOS: CocoaMQTT admite MQTT 5, TLS y WebSocket. Consulta MQTT en iOS con Swift.
- Android: Paho Java o el cliente de HiveMQ desde Kotlin, idealmente dentro de un componente consciente del ciclo de vida.
- Flutter:
mqtt_clientcubre destinos de servidor y navegador desde una única base de código Dart. Consulta el tutorial de Flutter MQTT. - React Native: MQTT.js sobre WebSocket es la vía habitual, con algunos polyfills. Consulta React Native MQTT.
Problemas que parecen errores de la librería
La mayoría de los problemas que se achacan a una librería son en realidad problemas de configuración o de diseño. Comprueba esto primero:
| Síntoma | Causa real | Solución |
|---|---|---|
| Bucle constante de conexión/desconexión | Dos procesos comparten un client ID | Genera IDs únicos; el broker expulsa la sesión más antigua |
| No llegan mensajes tras reconectar | Sesión limpia y sin volver a suscribirse | Suscríbete en el callback de conexión, no una sola vez al arrancar |
| El bucle de eventos se congela | Trabajo bloqueante dentro de un callback de mensaje | Delega el trabajo en una cola, un pool de hilos o una tarea asíncrona |
| Payloads grandes descartados sin aviso | Límite del buffer del cliente o del tamaño en el broker | Amplía el buffer del cliente; respeta los límites del broker |
| Falla el handshake TLS en el dispositivo | Falta la CA raíz o el reloj es incorrecto | Instala el paquete de CA y sincroniza la hora por NTP antes de conectar |
| Duplicados con QoS 1 | Comportamiento esperado de "al menos una vez" | Haz que los manejadores sean idempotentes; consulta los niveles de QoS en MQTT explicados |
La gestión de la reconexión merece especial atención en Python; la guía de reconexión de paho-mqtt explica los ajustes de backoff. Para un enfoque sistemático de verificación de un cliente antes de lanzarlo, lee cómo probar clientes MQTT, y ten a mano los ejemplos de mosquitto_pub y mosquitto_sub como segundo cliente independiente al depurar.
Del broker público a tu propio broker
Todos los fragmentos anteriores usan el servidor MQTT público gratuito, anónimo y perfecto para comparar librerías lado a lado: ejecuta el suscriptor de Python, publica desde Go y observa cómo llega el mensaje. Sin embargo, el servidor público es para pruebas y prototipos, es compartido y cualquiera puede leerlo, así que los clientes de producción necesitan credenciales y un broker aislado.
ConnectMQ es un broker MQTT gestionado que funciona con todas las librerías de esta guía, porque habla MQTT 3.1.1 y 5.0 estándar sobre TCP 1883, TLS 8883, WebSocket 8083 y WebSocket seguro 8084 (ruta /mqtt). Cuando creas un broker en el portal recibes un nombre de usuario, una contraseña y un client ID. Adaptar cualquiera de los fragmentos anteriores es un cambio de tres líneas, como se muestra aquí para paho-mqtt:
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID", protocol=mqtt.MQTTv5)
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()
ConnectMQ vincula los client IDs a las credenciales emitidas, así que usa el ID generado exactamente como se te entrega. Si aún estás decidiendo cómo alojar tu broker, nuestra guía del panorama de brokers MQTT en la nube compara las opciones serverless, dedicadas y en tu propia nube.
Conecta cualquier librería MQTT a ConnectMQ
Elige la librería que encaje con tu stack, ejecuta su fragmento contra broker.connectmq.com y, cuando llegue el momento de usar topics privados, crea un broker gratuito en app.connectmq.com. El plan Serverless gratuito no requiere tarjeta de crédito y te ofrece hasta 25 conexiones, QoS hasta 1 y mensajes de 256 KB, más que suficiente para construir y probar clientes en varios lenguajes a la vez.
Preguntas frecuentes
¿Cuál es la mejor librería cliente MQTT?
No hay una única mejor; la elección correcta es la librería mantenida que encaje con tu lenguaje y tu modelo de concurrencia. paho-mqtt para Python, MQTT.js para JavaScript, MQTTnet para .NET y Paho o rumqttc para Go y Rust son las opciones por defecto habituales y bien soportadas.
¿Todas las librerías cliente MQTT admiten MQTT 5?
No. Varias populares, como PubSubClient para Arduino y el cliente clásico Paho Go, solo implementan MQTT 3.1.1. Si necesitas suscripciones compartidas, caducidad de mensajes o reason codes, confirma el soporte de MQTT 5 antes de adoptar una librería.
¿Pueden comunicarse clientes escritos en lenguajes distintos?
Sí. El broker enruta bytes, no objetos de un lenguaje, así que un publicador en Python y un suscriptor en C# interoperan siempre que se pongan de acuerdo en los topics y el formato del payload. JSON es el formato común más fácil para empezar.
¿Qué librería debo usar en el navegador?
MQTT.js sobre WebSocket seguro es la opción estándar. Conéctate a wss://broker.connectmq.com:8084/mqtt, porque los navegadores no pueden abrir sockets TCP en bruto y las páginas HTTPS bloquean las conexiones WebSocket inseguras.
¿Por qué mi cliente funciona en el broker público pero no en uno privado?
Normalmente faltan las credenciales, el client ID no coincide con el emitido o TLS no está activado. Revisa el reason code del CONNACK que informa tu librería; te dice exactamente cuál de estas cosas falla.



