C语言MQTT学习系列(3篇):第一篇:从零开始学MQTT(C语言版):入门必看,跑通最简Demo

前言

最近开始深耕TCP/IP相关开发,给自己定了个具体目标:用C语言写MQTT程序,实现两个客户端互发消息------A客户端发布消息到/test主题,B客户端能成功接收。作为MQTT初学者,踩了不少二进制报文拼接、TCP连接的坑,整理了这份入门笔记,帮和我一样的新手快速上手,跳过无效摸索,重点吃透核心规则,快速跑通最简Demo。

本文核心目标:掌握MQTT入门必备规则、C语言前置技能,成功用C语言实现MQTT客户端与公共服务器的连接,完成"连接-接收确认"的核心流程。

一、先吃透4个MQTT核心规则

MQTT是基于TCP的二进制协议,C语言手写MQTT程序,本质就是手动拼接二进制报文,所以先把底层通信规则摸透,后续写代码才不会无从下手。入门阶段,不用记太多复杂内容,重点掌握这4点即可:

  1. 底层依赖:MQTT = TCP连接 + 二进制报文流,默认非加密端口1883,所有通信都基于TCP socket,没有TCP连接,就没有MQTT通信。

  2. 报文结构(核心中的核心):所有MQTT指令(连接、发消息、订阅)都遵循统一格式------固定头部(1~2字节) + 可变头部 + 有效载荷(payload)。其中固定头部是基础:第一个字节是报文类型(比如CONNECT连接报文是0x10,PUBLISH发布报文是0x30),第二个字节是剩余长度(C语言必须手动计算,入门阶段先记"剩余长度=可变头部+有效载荷的总长度")。

  3. 入门优先用QoS0:QoS0是"最多一次"的消息传输模式,发出去就不管,没有回执、没有重发,是C语言手写最简洁的模式,QoS1、QoS2后续再逐步扩展。

  4. 通信模式:MQTT是严格的"请求-响应"模式,客户端发请求报文,服务器必须回响应报文,比如发CONNECT连接报文,就必须等服务器回CONNACK确认报文,否则后续操作都会失败。


二、C语言前置技能(硬性要求,缺一不可)

MQTT基于TCP,C语言写MQTT程序,必须先掌握TCP socket编程和二进制操作,这是基础中的基础,没掌握的话,写代码会全程卡壳。

1. Linux Socket基础(优先学Linux,Windows可兼容Winsock)

核心掌握4个函数,就是MQTT通信的"四大基石":

  • socket():创建套接字,相当于打开电脑的网络功能,返回一个文件描述符(sockfd),后续所有网络操作都依赖这个描述符。

  • connect():主动连接MQTT服务器,需要传入服务器IP和端口(默认1883),连接成功才算真正打通通信通道。

  • send():发送二进制报文,MQTT的所有指令(CONNECT、PUBLISH等),都是通过这个函数发送到服务器。

  • recv():接收服务器的响应报文,比如CONNACK、SUBACK等,必须通过这个函数读取服务器的回复,否则会导致缓冲区堆积,程序异常。

2. 二进制/字节操作

MQTT传输的是纯字节流,C语言中主要用3个函数拼接、清空报文:memcpy(拼接字节)、memset(清空缓冲区)、uint8_t(无符号char,存储单个字节,避免符号干扰)。

3. 网络字节序(大端序)

MQTT协议规定,所有数字(比如端口号、保活时间)必须用大端序传输,而C语言主机默认是小端序,所以必须用htons()函数进行转换(比如端口1883,要转换成htons(1883)才能传输)。

三、核心步骤:跑通最简MQTT Demo(入门成功的标志)

最简Demo的目标很简单:只做3件事,不添加任何多余功能,确保能成功连接MQTT服务器,接收服务器的连接确认。

1. 准备工具

  • 公共MQTT服务器:test.mosquitto.org(免费、无门槛,无需注册账号,直接可用,默认端口1883)。

  • 调试工具(可选):MQTTX(桌面端,用来验证服务器是否可用,后续可验证自己写的客户端是否正常)。

  • 编译环境:Linux(Ubuntu最佳),gcc编译器。

2. 编写最简Demo代码(C语言,可直接复制运行)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MQTT_SERVER "test.mosquitto.org"  // 公共MQTT服务器
#define MQTT_PORT 1883                    // MQTT默认端口

int main() {
    // 1. 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket创建失败");
        return -1;
    }

    // 2. 配置服务器地址
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;                // IPv4协议
    addr.sin_port = htons(MQTT_PORT);         // 端口转换为大端序
    inet_pton(AF_INET, MQTT_SERVER, &addr.sin_addr);  // 服务器IP转换

    // 3. 连接MQTT服务器
    if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("连接服务器失败");
        close(sockfd);
        return -1;
    }
    printf("TCP连接服务器成功!\n");

    // 4. 拼接并发送CONNECT连接报文(二进制)
    // 报文解析:固定头(0x10=CONNECT, 0x13=剩余长度19) + 可变头 + 有效载荷(客户端ID)
    unsigned char connect_pkt[] = {
        0x10, 0x13,                   // 固定头
        0x00,0x04,'M','Q','T','T',   // 协议名(MQTT)+ 协议名长度
        0x04,                         // 协议级别(3.1.1)
        0x02,                         // 连接标志(Clean Session)
        0x00, 60,                     // 保活时间60秒(大端序)
        0x00,0x07,'c','l','i','e','n','t','0'  // 客户端ID(自定义,唯一即可)
    };
    send(sockfd, connect_pkt, sizeof(connect_pkt), 0);
    printf("已发送MQTT连接报文\n");

    // 5. 接收服务器回复的CONNACK报文(必须接收,否则程序异常)
    char buf[1024];
    int recv_len = recv(sockfd, buf, sizeof(buf), 0);
    if (recv_len <= 0) {
        printf("未收到CONNACK报文,连接失败\n");
        close(sockfd);
        return -1;
    }

    // 判断连接是否成功(CONNACK报文:0x20=报文类型,0x00=连接成功返回码)
    if (buf[0] == 0x20 && buf[3] == 0x00) {
        printf("MQTT连接成功!✅\n");
    } else {
        printf("MQTT连接失败,返回码:%d\n", buf[3]);
        close(sockfd);
        return -1;
    }

    // 断开连接
    unsigned char disconnect_pkt[] = {0xE0, 0x00};  // DISCONNECT报文
    send(sockfd, disconnect_pkt, sizeof(disconnect_pkt), 0);
    close(sockfd);
    printf("已断开连接\n");

    return 0;
}

3. 编译运行步骤

  1. 保存代码为mqtt_demo.c;

  2. 编译命令:gcc mqtt_demo.c -o mqtt_demo;

  3. 运行命令:./mqtt_demo;

  4. 成功标志:打印"TCP连接服务器成功!""MQTT连接成功!✅",说明最简Demo跑通。

四、入门避坑点(新手必看)

  • 连接服务器失败:检查网络是否正常,test.mosquitto.org是否能ping通,端口1883是否被防火墙拦截。

  • 未收到CONNACK报文:大概率是CONNECT报文拼接错误,重点检查"剩余长度"是否计算正确(剩余长度=可变头部+有效载荷的总长度)。

  • 端口转换错误:忘记用htons()转换端口,会导致连接失败,记住所有网络传输的数字都要转大端序。

小结

入门MQTT,核心不是死记硬背API,而是吃透 "TCP连接+二进制报文" 的底层逻辑,跑通最简Demo就是入门成功的标志。下一篇我们将在此基础上,实现MQTT的发布(PUBLISH)和订阅(SUBSCRIBE) 功能,完成两个客户端的互发通信。

关注我,后续持续更新MQTT实战内容,一起从入门到精通!


相关推荐
深邃-1 小时前
【Web安全】-计算机网络协议(2):请求方法,头部字段,DNS协议详解
linux·网络·网络协议·计算机网络·安全·web安全·网络安全
上海云盾-小余9 小时前
域名解析被劫持怎么办?DNS 安全防护与异常修复全教程
网络·安全·ddos
科技风向标go10 小时前
**2026年Q2中国消费级监控摄像头市场观察:存量时代的竞争逻辑重构**
网络·安全·监控·户外安防
原来是猿10 小时前
网络计算器:理解序列化与反序列化(中)
linux·运维·服务器·网络·tcp/ip
Cat_Rocky11 小时前
k8s-持久化存储,粗浅学习
java·学习·kubernetes
知识领航员11 小时前
蘑兔AI音乐深度实测:功能拆解、实测表现与适用场景
java·c语言·c++·人工智能·python·算法·github
AOwhisky11 小时前
虚拟化技术学习笔记
linux·运维·笔记·学习·虚拟化技术
一只机电自动化菜鸟12 小时前
一建机电备考笔记(33) 机电专业技术(起重技术-吊装方案)(含考频+题型)
经验分享·笔记·学习·职场和发展·课程设计
汪汪大队u12 小时前
续:从 Docker Compose 到 Kubernetes(2)—— 服务优化与排错
网络·后端·物联网·struts·容器