黔西南布依族苗族自治州网站建设_网站建设公司_数据统计_seo优化
2026/1/12 6:29:09 网站建设 项目流程

1.三大通信协议:TCP/IP、CAN与MQTT详解

网络通信协议的基础实现涉及多个层面,从物理层到应用层,不同协议在不同层级发挥作用。以下以TCP/IPCANMQTT为例,分别介绍它们的基础实现原理和关键要素:


一、TCP/IP 协议栈(面向互联网通信)

1. 分层结构

TCP/IP 模型通常分为四层:

  • 链路层(Link Layer):如以太网、Wi-Fi,负责物理传输。
  • 网络层(Internet Layer):IP 协议(IPv4/IPv6),负责寻址与路由。
  • 传输层(Transport Layer):TCP(可靠)、UDP(不可靠但高效)。
  • 应用层(Application Layer):HTTP、FTP、SMTP 等。

2. 基础实现要点

IP 层
  • 实现 IP 数据包的封装与解析。
  • 维护路由表,决定下一跳。
  • 处理分片与重组(IPv4)。
TCP 层
  • 三次握手建立连接,四次挥手断开连接。
  • 序列号、确认应答(ACK)、超时重传机制保障可靠性。
  • 流量控制(滑动窗口)和拥塞控制(如 Reno、CUBIC)。
  • 在操作系统内核中实现(如 Linux 的 net/ipv4/tcp.c)。
用户态编程接口
// 简化的 TCP 客户端示例(Linux socket API) int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr = { .sin_family = AF_INET, .sin_port = htons(8080) }; inet_pton(AF_INET, "192.168.1.1", &server_addr.sin_addr); connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); send(sockfd, "Hello", 5, 0);

实际协议栈由操作系统提供,用户通过 Socket API 调用。


二、CAN(Controller Area Network,用于嵌入式/汽车)

1. 特点

  • 物理层 + 数据链路层协议(OSI 模型下两层)。
  • 差分信号抗干扰,支持多主架构。
  • 报文标识符(ID)决定优先级,非地址寻址。

2. 基础实现

硬件依赖
  • 需要CAN 控制器(如 STM32 内置 bxCAN) +CAN 收发器(如 MCP2551)。
软件实现步骤
  1. 初始化 CAN 控制器(波特率、工作模式)。
  2. 配置过滤器(只接收特定 ID 报文)。
  3. 发送:将数据填入 CAN 帧(标准帧 11-bit ID / 扩展帧 29-bit ID)。
  4. 接收:中断或轮询读取 RX 缓冲区。
// STM32 HAL 示例(简化) CAN_TxHeaderTypeDef txHeader = { .StdId = 0x123, .IDE = CAN_ID_STD, .RTR = CAN_RTR_DATA, .DLC = 8 }; uint8_t data[8] = {1,2,3,4,5,6,7,8}; HAL_CAN_AddTxMessage(&hcan, &txHeader, data, &txMailbox);

CAN 协议本身不包含重传、加密等高级功能,需上层协议(如 CANopen、J1939)补充。


三、MQTT(Message Queuing Telemetry Transport,物联网应用层协议)

1. 特点

  • 基于TCP/IP发布/订阅模式。
  • 轻量(最小报文仅 2 字节),适合低带宽、不稳定网络。
  • 支持 QoS 0/1/2(至多一次、至少一次、恰好一次)。

2. 基础实现

协议结构
  • 固定头部(1~5 字节)+ 可变头部 + 负载。
  • 控制报文类型:CONNECT、PUBLISH、SUBSCRIBE、PINGREQ 等。
客户端实现逻辑
  1. 建立 TCP 连接(通常到 1883 端口)。
  2. 发送CONNECT报文(含 ClientID、用户名、密码等)。
  3. 接收CONNACK确认。
  4. 发布/订阅主题(PUBLISH/SUBSCRIBE)。
  5. 定期发送PINGREQ保活。
示例(使用开源库如 Eclipse Paho)
import paho.mqtt.client as mqtt def on_connect(client, userdata, flags, rc): client.subscribe("sensor/temp") def on_message(client, userdata, msg): print(f"{msg.topic}: {msg.payload}") client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message client.connect("broker.hivemq.com", 1883) client.loop_forever()

MQTT 依赖底层 TCP/IP,因此需先有网络栈支持(如 lwIP 在嵌入式系统中)。


总结对比

协议层级可靠性典型场景是否需操作系统
TCP/IP网络+传输层高(TCP)互联网通信是(或轻量 TCP/IP 栈)
CAN物理+数据链路层中(无重传)汽车、工业控制否(裸机即可)
MQTT应用层依赖 TCP + QoSIoT 设备云通信通常需要(或 RTOS + TCP/IP)

2.写一个简易 TCP 或 MQTT 客户端 c++

下面是一个从零开始(不依赖第三方库)用 C++ 编写的简易 TCP 客户端,并在此基础上扩展为一个最小可用的 MQTT 客户端(仅支持 CONNECT + PUBLISH,QoS 0)。

✅ 目标平台:Linux / macOS(使用 POSIX socket API)
✅ 语言:C++11 或更高
✅ 功能:连接 MQTT Broker(如broker.hivemq.com:1883),发送一条 QoS 0 消息


第一步:简易 TCP 客户端(基础)

// tcp_client.hpp #pragma once #include <string> #include <vector> class TcpClient { public: TcpClient(const std::string& host, int port); ~TcpClient(); bool connect(); bool send(const std::vector<uint8_t>& data); std::vector<uint8_t> receive(size_t max_bytes = 1024); private: std::string host_; int port_; int sockfd_ = -1; };
// tcp_client.cpp #include "tcp_client.hpp" #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <cstring> #include <iostream> TcpClient::TcpClient(const std:: std::string& host, int port) : host_(host), port_(port) {} TcpClient::~TcpClient() { if (sockfd_ >= 0) close(sockfd_); } bool TcpClient::connect() { struct addrinfo hints{}, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(host_.c_str(), std::to_string(port_).c_str(), &hints, &res) != 0) { perror("getaddrinfo"); return false; } sockfd_ = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd_ == -1) { perror("socket"); freeaddrinfo(res); return false; } if (::connect(sockfd_, res->ai_addr, res->ai_addrlen) == -1) { perror("connect"); close(sockfd_); sockfd_ = -1; freeaddrinfo(res); return false; } freeaddrinfo(res); return true; } bool TcpClient::send(const std::vector<uint8_t>& data) { if (sockfd_ == -1) return false; ssize_t sent = ::send(sockfd_, data.data(), data.size(), 0); return static_cast<size_t>(sent) == data.size(); } std::vector<uint8_t> TcpClient::receive(size_t max_bytes) { std::vector<uint8_t> buffer(max_bytes); ssize_t n = recv(sockfd_, buffer.data(), max_bytes, 0); if (n <= 0) return {}; buffer.resize(n); return buffer; }

第二步:实现最小 MQTT 客户端(QoS 0 PUBLISH)

MQTT 报文格式(简化):

  • 固定头:1 字节(类型 + flags)
  • 剩余长度:1~4 字节(可变长度编码)
  • 可变头 + 负载

我们只实现:

  1. CONNECT(客户端标识符)
  2. PUBLISH(QoS 0,无应答)
// mqtt_client.hpp #pragma once #include "tcp_client.hpp" #include <string> #include <vector> class MqttClient { public: MqttClient(const std::string& broker_host, int broker_port, const std::string& client_id); bool connect(); bool publish(const std::string& topic, const std::string& payload); private: TcpClient tcp_; std::string client_id_; std::vector<uint8_t> encode_connect(); std::vector<uint8_t> encode_publish(const std::string& topic, const std::string& payload); std::vector<uint8_t> encode_string(const std::string& str); void encode_remaining_length(std::vector<uint8_t>& buf, size_t len); };
// mqtt_client.cpp #include "mqtt_client.hpp" #include <vector> #include <string> #include <iostream> MqttClient::MqttClient(const std::string& broker_host, int broker_port, const std::string& client_id) : tcp_(broker_host, broker_port), client_id_(client_id) {} // 编码字符串:先写 2 字节长度(大端),再写内容 std::vector<uint8_t> MqttClient::encode_string(const std::string& str) { std::vector<uint8_t> result; uint16_t len = htons(static_cast<uint16_t>(str.size())); result.push_back(static_cast<uint8_t>(len >> 8)); result.push_back(static_cast<uint8_t>(len & 0xFF)); result.insert(result.end(), str.begin(), str.end()); return result; } // 可变字节编码剩余长度(最多 4 字节) void MqttClient::encode_remaining_length(std::vector<uint8_t>& buf, size_t len) { do { uint8_t encoded = len % 128; len /= 128; if (len > 0) encoded |= 128; buf.push_back(encoded); } while (len > 0); } std::vector<uint8_t> MqttClient::encode_connect() { std::vector<uint8_t> variable_header; // Protocol name "MQTT" variable_header.insert(variable_header.end(), {0x00, 0x04, 'M', 'Q', 'T', 'T'}); // Protocol level 4 (3.1.1) variable_header.push_back(0x04); // Connect flags: clean session = 1 (bit 1), no username/password variable_header.push_back(0x02); // 0000 0010 // Keep alive: 60 seconds variable_header.push_back(0x00); variable_header.push_back(0x3C); // 60 auto payload = encode_string(client_id_); size_t remaining_len = variable_header.size() + payload.size(); std::vector<uint8_t> packet; packet.push_back(0x10); // CONNECT packet type encode_remaining_length(packet, remaining_len); packet.insert(packet.end(), variable_header.begin(), variable_header.end()); packet.insert(packet.end(), payload.begin(), payload.end()); return packet; } std::vector<uint8_t> MqttClient::encode_publish(const std::string& topic, const std::string& payload) { auto topic_encoded = encode_string(topic); std::vector<uint8_t> packet; size_t remaining_len = topic_encoded.size() + payload.size(); packet.push_back(0x30); // PUBLISH, QoS=0, retain=0 encode_remaining_length(packet, remaining_len); packet.insert(packet.end(), topic_encoded.begin(), topic_encoded.end()); packet.insert(packet.end(), payload.begin(), payload.end()); return packet; } bool MqttClient::connect() { if (!tcp_.connect()) { std::cerr << "Failed to connect to broker\n"; return false; } auto connect_packet = encode_connect(); if (!tcp_.send(connect_packet)) { std::cerr << "Failed to send CONNECT\n"; return false; } // 简化:不处理 CONNACK(实际应读取 2 字节确认) auto response = tcp_.receive(4); if (response.size() < 2 || response[0] != 0x20 || response[1] != 0x02) { std::cerr << "Invalid CONNACK\n"; return false; } std::cout << "Connected to MQTT broker\n"; return true; } bool MqttClient::publish(const std::string& topic, const std::string& payload) { auto pub_packet = encode_publish(topic, payload); if (!tcp_.send(pub_packet)) { std::cerr << "Failed to send PUBLISH\n"; return false; } std::cout << "Published to '" << topic << "': " << payload << "\n"; return true; }

第三步:主程序示例

// main.cpp #include "mqtt_client.hpp" #include <thread> #include <chrono> int main() { // 使用公共 MQTT Broker(无需认证) MqttClient client("broker.hivemq.com", 1883, "cpp_mqtt_test_client"); if (!client.connect()) { return 1; } client.publish("test/topic/cpp", "Hello from minimal C++ MQTT client!"); // 等待一点时间让消息发出(实际应用中可保持连接) std::this_thread::sleep_for(std::chrono::seconds(1)); return 0; }

编译命令

g++ -std=c++11 -o mqtt_client main.cpp mqtt_client.cpp tcp_client.cpp

注意事项

  1. 无 TLS 支持:此版本使用明文 MQTT(端口 1883)。生产环境建议使用 TLS(需 OpenSSL)。
  2. 无重连/保活:未实现PINGREQ,长时间连接会断开。
  3. 无错误恢复:CONNACK 校验简单,实际应解析返回码。
  4. 仅 QoS 0:不处理 PUBACK 等确认机制。

扩展建议

  • 添加SUBSCRIBE和消息回调
  • 支持用户名/密码
  • 实现自动重连
  • 使用select()或线程处理接收

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询