MQTT Clients, Brokers & Connection Establishment Explained

By | May 24, 2026

Every MQTT system is built from two kinds of participant: clients and a broker. A client is any device that speaks MQTT; the broker is the central hub that every message passes through. Before any messages can flow, a client must establish a connection to the broker, and that handshake, the CONNECT and CONNACK exchange, carries the settings that govern how the rest of the session behaves.

This article explains what clients and brokers are and what each does, then walks through connection establishment in full: how the TCP-based handshake works, how it behaves through NAT and firewalls, every meaningful field in the CONNECT packet, and how to read the broker’s CONNACK reply including its return codes. It is a technical reference; mechanisms introduced here such as sessions, Last Will, and keep alive each have their own dedicated articles in this category.

Connection at a glance

ItemDetail
TransportTCP/IP; TLS for encryption; WebSockets for browsers
Default ports1883 (plain), 8883 (TLS), 80/443 (WebSockets)
Who initiatesAlways the client; the broker never connects to a client
Opening packetCONNECT (client → broker)
Reply packetCONNACK (broker → client)
Connection topologyAlways one client to the broker; clients never connect to each other
Connection lifetimeStays open until the client disconnects or the link breaks
Identifies the clientClientId

The MQTT client

An MQTT client is any device that runs an MQTT library and connects to a broker over a network. That definition is deliberately broad, and it is worth taking it at face value, because the range of things that qualify is enormous.

At one end, a client can be a very small, resource-constrained device: a microcontroller on a wireless link, running a bare-minimum MQTT library, with only a few kilobytes of memory to spare. At the other end, a client can be a full-fledged server, a gateway aggregating data from many devices, or an ordinary computer running a graphical MQTT tool for testing. What matters is not the size or power of the device but whether it speaks MQTT over a TCP/IP stack. If it does, it is a client.

This breadth is possible because the client side of MQTT is intentionally simple and streamlined. The protocol was designed so that implementing a client is straightforward, which is one of the central reasons MQTT suits small devices. A minimal client does not need to understand the full machinery of the broker; it needs to connect, publish, subscribe, and handle a handful of acknowledgement packets.

Publishers and subscribers are roles, not types

A frequent point of confusion is the relationship between “client,” “publisher,” and “subscriber.” Publisher and subscriber are not separate kinds of device. They are labels describing what a client is doing at a given moment. A client that is currently sending messages is acting as a publisher; a client that is currently receiving messages is acting as a subscriber.

Crucially, the same client can do both, often at the same time. A field device might publish its sensor readings to one topic while subscribing to a command topic to receive instructions. There is no architectural distinction between a “publisher device” and a “subscriber device”; there are only clients, each of which publishes, subscribes, or both, as its application requires.

Client libraries

Because the client implementation is simple, mature MQTT client libraries exist for essentially every language and platform a developer might use. These include Android, Arduino, C, C++, C#, Go, iOS, Java, JavaScript, Python, and .NET, among many others. This wide availability means you rarely implement the protocol yourself; you pick a library for your platform and work against its API. Individual libraries also add their own configuration options on top of the protocol’s, such as how queued messages are stored locally, automatic reconnection behavior, or threading models.

The MQTT broker

The broker is the counterpart of the client and the heart of any publish/subscribe system. Where clients sit at the edges, the broker sits at the center, and every single message must pass through it. Depending on the implementation and the hardware it runs on, a broker can handle anywhere from a handful of concurrently connected clients to many hundreds of thousands or more.

The broker’s responsibilities are substantial:

  • Receiving all messages from every connected publisher.
  • Filtering messages to determine which clients are subscribed to each one.
  • Determining who is subscribed to each message and delivering it to those clients, at the Quality of Service each requested.
  • Holding the sessions of all persistent clients, including their subscriptions and any messages they missed while offline.
  • Authenticating and authorizing clients, controlling who may connect and which topics each may publish to or subscribe from.

Most brokers are extensible, which is important in practice. Extensibility lets operators plug in custom authentication and authorization schemes and integrate the broker with backend systems. That integration matters because the broker is frequently the component directly exposed to the internet: it terminates connections from large numbers of field clients and then passes messages on to downstream systems for analysis, storage, and processing.

Because every message flows through it, the broker’s qualities largely determine the qualities of the whole system. A production broker needs to be highly scalable, easy to monitor with standard tooling, straightforward to integrate with backend infrastructure, and resilient to failure. In large deployments, a single broker instance is replaced by a cluster of broker nodes that share the connection and message load behind load balancers, which is how MQTT systems reach very high connection counts.

How an MQTT connection works

MQTT is built on top of TCP/IP. Both the client and the broker need a TCP/IP stack to communicate. In terms of the OSI model, MQTT occupies the upper application layers (5 through 7), TCP provides the transport at layer 4, and IP provides addressing and routing at layer 3. TCP gives MQTT a reliable, ordered, error-checked byte stream to work over, which the protocol builds its own guarantees on top of.

An MQTT connection is always between one client and the broker. Clients never connect to each other directly, even when one client’s messages are ultimately destined for another. All communication is mediated by the broker.

The handshake itself is simple. To open a connection, the client establishes a TCP connection to the broker and sends a CONNECT message. The broker processes it and responds with a CONNACK (connect acknowledgement) message carrying a status code. Once that exchange completes successfully, the connection is established, and the broker keeps it open until one of two things happens: the client sends a disconnect command, or the connection breaks.

That persistent, long-lived connection is a defining characteristic of MQTT. Unlike a protocol that opens a connection per request, MQTT opens one connection and keeps it open, using it for bidirectional traffic for as long as the client remains connected. This is what allows the broker to push messages to a client at any time, the instant they arrive, without the client having to poll.

MQTT connections through NAT

A very common real-world concern is that MQTT clients sit behind a router performing network address translation (NAT), which translates a private network address such as 192.168.x.x or 10.0.x.x into a single public-facing address. Devices behind NAT cannot normally be reached by inbound connections from outside, which would be a problem for many protocols.

For MQTT it is not a problem at all. The reason is the direction of the handshake. The client always initiates the connection, sending its CONNECT outbound to the broker’s public address. Because the broker has a public address and keeps the connection open after the initial CONNECT, that single open connection then carries traffic in both directions, including messages the broker pushes down to the client. The broker never needs to initiate a connection to the client, so the fact that the client is behind NAT, or behind a firewall that blocks inbound connections, makes no difference. This is one of the practical benefits of the space-decoupling property of publish/subscribe.

Protecting the broker during connection

The broker also uses the connection step to protect itself from misbehaving or malicious clients. If a CONNECT message is malformed according to the specification, or if too much time passes between the client opening the network socket and actually sending the CONNECT, the broker closes the connection. This behavior deters clients that would otherwise hold a socket open without completing a connection, a tactic that could slow the broker down or tie up its resources. A well-behaved client opens the socket and promptly sends a valid CONNECT.

Inside the CONNECT packet

The CONNECT message carries a number of fields. Some of these are mostly of interest to the people implementing an MQTT library rather than to people using one, but several directly affect application behavior and are worth understanding in detail. The fields below are the ones that matter most in practice.

ClientId

The client identifier, or ClientId, identifies each client that connects to the broker. The broker uses the ClientId to identify the client and to track its current state. Because of this role, the ClientId should be unique per client and broker; two clients connecting with the same ClientId will conflict, and the broker will typically perform a take-over, closing one in favor of the other.

In MQTT 3.1.1, you are allowed to send an empty ClientId if you do not need the broker to hold any state for you. An empty ClientId results in a connection without any state attached. In that case, however, the clean session flag must be set to true, or the broker will reject the connection, because a stateless connection and a request for persistent state are contradictory.

Clean Session

The clean session flag tells the broker whether the client wants a persistent session. Its two values produce very different behavior:

  • With CleanSession = false, the client requests a persistent session. The broker stores all of the client’s subscriptions, and it stores any missed messages for the client that were published at QoS 1 or 2. When the client reconnects, this state is restored.
  • With CleanSession = true, the session is not persistent. The broker stores nothing for the client and purges any information left over from a previous persistent session for that ClientId.

This flag is the entry point to MQTT’s session model, which has significant implications for reliability and resource use. The persistent sessions article covers exactly what is stored, when, and why, and MQTT 5 refines this mechanism into the Clean Start flag plus a session expiry interval.

Username and Password

MQTT can carry a username and password in the CONNECT for client authentication and authorization. Both are optional. There is one critical caveat to understand: unless this information is encrypted or hashed, either by the application or by an encrypted transport such as TLS, the password is sent in plain text over the wire. For that reason, the strong recommendation is to use usernames and passwords only together with secure transport. Some brokers can authenticate clients using a TLS client certificate instead, in which case no username and password are needed at all. Security is covered more fully in the flagship overview and the enhanced authentication article.

Will message (Last Will and Testament)

The will message is part of MQTT’s Last Will and Testament (LWT) feature, which notifies other clients when a client disconnects ungracefully. When a client connects, it can register a last will, in the form of an MQTT message and topic, inside the CONNECT message. If the client later disconnects ungracefully, the broker publishes that stored last will message on the client’s behalf to the will topic. The relevant CONNECT fields are the last will topic, last will QoS, last will message (the payload), and last will retain flag. The full mechanism, including when the broker decides a disconnect was ungraceful, is covered in the Last Will and Testament article.

Keep Alive

The keep alive is a time interval, in seconds, that the client specifies and communicates to the broker when the connection is established. It defines the longest period of time that the broker and client can go without exchanging a message. The client commits to sending regular PINGREQ messages to the broker, and the broker responds with a PINGRESP. This back-and-forth lets each side determine that the other is still available, even when there is no application traffic to send. Keep alive is the mechanism that detects dead or half-open connections, and it is covered in full in the keep alive article.

What else is in there

The fields above are essentially everything you need to connect a client to a broker. Beyond them, individual libraries often expose additional configuration options that are not part of the wire protocol itself, such as how a specific implementation stores queued messages, how it handles automatic reconnection, or how it schedules callbacks. Those are library concerns layered on top of the protocol’s CONNECT fields.

Inside the CONNACK packet

When a broker receives a CONNECT message, it is obligated to respond with a CONNACK message. The CONNACK is how the broker tells the client whether the connection succeeded and what state it found. It carries two key pieces of information.

Session present flag

The session present flag tells the client whether the broker already has a persistent session available for it from previous interactions. Reading this flag correctly lets a reconnecting client decide whether it needs to re-subscribe to its topics or whether its subscriptions are still held by the broker.

Its value depends on the clean session request:

  • When a client connects with Clean Session set to true, the session present flag is always false, because by definition there is no session being kept.
  • When a client connects with Clean Session set to false, there are two possibilities. If the broker has stored session information for that ClientId, the session present flag is true. If the broker has no session information for that ClientId, the flag is false.

In short, a true value tells the reconnecting client “I still have your subscriptions and state,” and a false value tells it “you are starting fresh; re-subscribe as needed.”

Connect acknowledge flag and return code

The second piece of information in the CONNACK is the connect acknowledge flag, which contains a return code telling the client whether the connection attempt succeeded and, if not, why. In MQTT 3.1.1 there is a small, fixed set of return codes:

Return codeMeaning
0Connection accepted
1Connection refused — unacceptable protocol version
2Connection refused — identifier rejected
3Connection refused — server unavailable
4Connection refused — bad username or password
5Connection refused — not authorized

A return code of 0 is success; the client is connected and can begin publishing and subscribing. Any non-zero code is a refusal, and the code indicates the reason. Code 1 means the broker does not support the protocol version the client requested. Code 2 means the ClientId was rejected, for example because it violates the broker’s rules. Code 3 means the broker is up but temporarily unable to service the connection. Code 4 means the credentials were wrong. Code 5 means the client authenticated but is not authorized to connect.

This limited set of codes was a known pain point in MQTT 3.1.1: when something went wrong, the client often had little detail to work with. MQTT 5 substantially expands the available reason codes and adds human-readable reason strings, giving clients far more insight into why a connection was refused. That improvement is covered in the improved client feedback article.

The full connection sequence

Putting the pieces together, a successful connection looks like this:

  1. The client opens a TCP (or TLS) connection to the broker’s address and port.
  2. The client sends a CONNECT packet containing its ClientId, clean session flag, optional credentials, optional last will, and keep alive interval.
  3. The broker validates the packet, authenticates and authorizes the client, and checks for existing session state.
  4. The broker replies with a CONNACK containing the session present flag and a return code.
  5. On return code 0, the connection is established and stays open. The client now publishes, subscribes, and sends periodic PINGREQ packets to satisfy keep alive, while the broker pushes any matching messages down to it.

From this point, the connection persists until the client disconnects gracefully with a DISCONNECT, or the connection breaks and the broker detects it via the keep alive mechanism, at which point it may publish the client’s last will.

How a connection ends

A connection that has been established stays open until it ends, and how it ends matters, because the broker behaves differently depending on the manner of the disconnect. There are two cases.

A graceful disconnect happens when the client deliberately closes the session by sending a DISCONNECT packet before closing the underlying TCP connection. The DISCONNECT tells the broker the client is leaving on purpose. In response, the broker discards any stored Last Will message for that client, because a clean, intentional departure is not the kind of event the last will is meant to announce. In MQTT 3.1.1 the DISCONNECT packet carries no payload and flows only from client to broker. MQTT 5 makes DISCONNECT bidirectional and lets it carry a reason code, so the broker can also tell a client why it is being disconnected.

An ungraceful disconnect happens when the client vanishes without sending a DISCONNECT. The causes are many: the network drops, a battery dies, the device crashes, or a protocol error forces the broker to close the connection. Because there was no DISCONNECT, the broker treats this as the unexpected loss the Last Will feature exists for, and it publishes the client’s stored last will message to the will topic, notifying other interested clients of the loss.

The broker does not always learn of an ungraceful disconnect immediately. If the TCP connection breaks in a way the broker detects, it reacts at once. But on unreliable links a connection can become half-open, appearing alive while silently discarding traffic. This is exactly what the keep alive mechanism is for: if the broker receives neither a message nor a PINGREQ from the client within one and a half times the keep alive interval, it concludes the client is gone, closes the connection, and sends the last will if one was set. The keep alive article covers this detection in detail.

The practical takeaway is that a client should always send a DISCONNECT when it intends to leave cleanly. Doing so suppresses the last will and lets the broker free resources promptly, rather than waiting out the keep alive timeout to discover the client is gone.

How this connects to the rest of MQTT

The CONNECT/CONNACK exchange is where many of MQTT’s other features are configured, so this handshake is a hub that several other topics radiate from:

  • The clean session flag and session present flag are the entry points to persistent sessions and message queuing.
  • The will fields set up Last Will and Testament.
  • The keep alive interval configures the dead-connection detection mechanism.
  • The username, password, and transport choice are the foundation of authentication and security.
  • The return codes are the simple ancestor of MQTT 5’s much richer reason codes and reason strings.

Understanding what each CONNECT field does, and how to read the CONNACK, gives you the vocabulary for almost every reliability and security decision you will make in an MQTT deployment.

Frequently asked questions

Who initiates an MQTT connection, the client or the broker?

Always the client. The client opens the TCP connection and sends the CONNECT packet; the broker only ever responds. The broker never initiates a connection to a client, which is why clients behind NAT or firewalls work without special configuration.

What is the difference between an MQTT client and broker?

A client is any device that runs an MQTT library and connects to send or receive messages. The broker is the central server that all clients connect to; it receives every message, filters and routes them to subscribers, holds session state, and handles authentication and authorization.

What is a ClientId in MQTT?

The ClientId is a string that uniquely identifies a client to the broker. The broker uses it to track the client and its session state, so it should be unique per broker. In MQTT 3.1.1 an empty ClientId is allowed only if the clean session flag is set to true.

What does the CONNACK return code mean?

It tells the client whether the connection was accepted. A return code of 0 means accepted. Non-zero codes indicate refusal: unacceptable protocol version (1), identifier rejected (2), server unavailable (3), bad username or password (4), or not authorized (5).

Can MQTT clients behind NAT or a firewall connect to a broker?

Yes. Because the client always initiates an outbound connection to the broker and the connection stays open for two-way traffic, NAT and inbound-blocking firewalls do not interfere. The broker never needs to reach the client directly.

What does the session present flag tell a client?

It tells the client whether the broker already holds a persistent session for it. If true, the broker still has the client’s subscriptions and state, so re-subscribing is unnecessary. If false, the client is starting fresh and should re-subscribe.

Author: Zakaria El Intissar

I'm an automation and industrial computing engineer with 12 years of experience in power system automation, SCADA communication protocols, and electrical protection. I build tools and write guides for Modbus, DNP3, IEC 101/103/104, and IEC 61850 on ScadaProtocols.com to help engineers decode, analyze, and troubleshoot real industrial communication systems.