<think>我们之前已经实现了基于Socket的通信框架,现在需要扩展以支持多种通信协议。根据问题,我们需要实现一个能够处理不同模块通信协议的C++系统。
设计思路:
1. 定义一个通用的通信协议接口(抽象基类),包含发送和接收数据的基本方法。
2. 针对不同的模块协议(如Modbus, CANopen over TCP, 自定义二进制协议等)实现具体的协议类。
3. 使用工厂模式根据配置创建对应的协议处理器。
4. 在Socket连接的基础上,使用协议处理器进行数据的封装和解析。
以下代码将实现:
- 协议接口(IProtocol)
- 两种示例协议:ModbusTCP和BinaryProtocol
- 协议工厂(ProtocolFactory)
- 扩展之前的ModuleCommunicator类,使其支持不同的协议
注意:为了简化,我们只实现基本框架,部分细节可能需要根据实际协议调整。
</think>### C++ 实现多协议 Socket 通信系统
以下是一个完整的 C++ 实现,支持多种通信协议(Modbus TCP、CANopen over TCP、自定义二进制协议)通过 Socket 与不同模块通信:
```cpp
#include <iostream>
#include <vector>
#include <map>
#include <memory>
#include <cstring>
#include <stdexcept>
#include <functional>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define SOCKET_ERROR_CODE WSAGetLastError()
#define CLOSE_SOCKET closesocket
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#define SOCKET_ERROR_CODE errno
#define CLOSE_SOCKET close
#define INVALID_SOCKET -1
#define SOCKET int
#endif
// ====================== 协议接口定义 ======================
class IProtocol {
public:
virtual ~IProtocol() = default;
// 序列化请求数据
virtual std::vector<uint8_t> serializeRequest(
const std::string& command,
const std::vector<uint8_t>& params) = 0;
// 解析响应数据
virtual bool parseResponse(
const std::vector<uint8_t>& data,
std::vector<uint8_t>& result) = 0;
// 获取协议名称
virtual std::string getName() const = 0;
// 获取协议所需的端口号
virtual uint16_t getDefaultPort() const = 0;
};
// ====================== Modbus TCP 协议实现 ======================
class ModbusTCPProtocol : public IProtocol {
public:
std::vector<uint8_t> serializeRequest(
const std::string& command,
const std::vector<uint8_t>& params) override {
// Modbus TCP报文结构: [事务ID(2)|协议ID(2)|长度(2)|单元ID(1)|功能码(1)|数据...]
std::vector<uint8_t> packet(8); // 头部固定7字节 + 单元ID1字节
// 事务ID (递增)
static uint16_t transaction_id = 1;
packet[0] = transaction_id >> 8;
packet[1] = transaction_id & 0xFF;
transaction_id++;
// 协议ID (0表示ModbusTCP)
packet[2] = 0;
packet[3] = 0;
// 单元ID (设备地址)
packet[6] = device_id;
// 功能码和数据处理
if (command == "READ_COILS") {
packet[7] = 0x01; // 功能码
// 参数: [起始地址(2), 线圈数量(2)]
if (params.size() != 4) {
throw std::invalid_argument("Invalid parameters for READ_COILS");
}
packet.insert(packet.end(), params.begin(), params.end());
} else if (command == "WRITE_REGISTER") {
packet[7] = 0x06; // 功能码
// 参数: [寄存器地址(2), 寄存器值(2)]
if (params.size() != 4) {
throw std::invalid_argument("Invalid parameters for WRITE_REGISTER");
}
packet.insert(packet.end(), params.begin(), params.end());
} else {
throw std::invalid_argument("Unsupported Modbus command: " + command);
}
// 设置长度字段 (单元ID + 功能码 + 数据)
uint16_t length = static_cast<uint16_t>(packet.size() - 6);
packet[4] = length >> 8;
packet[5] = length & 0xFF;
return packet;
}
bool parseResponse(const std::vector<uint8_t>& data, std::vector<uint8_t>& result) override {
// 最小响应长度: 事务ID(2) + 协议ID(2) + 长度(2) + 单元ID(1) + 功能码(1)
if (data.size() < 8) return false;
// 检查协议ID
if (data[2] != 0 || data[3] != 0) return false;
// 检查单元ID
if (data[6] != device_id) return false;
// 解析功能码 (最高位为1表示错误)
uint8_t func_code = data[7];
if (func_code & 0x80) {
// 错误响应: [异常码(1)]
if (data.size() < 9) return false;
uint8_t error_code = data[8];
result = {func_code, error_code};
return false;
}
// 成功响应处理
switch (func_code) {
case 0x01: // 读线圈
if (data.size() < 9) return false;
result.assign(data.begin() + 8, data.end());
break;
case 0x06: // 写寄存器
// 响应内容与请求相同: [寄存器地址(2), 寄存器值(2)]
if (data.size() < 12) return false;
result.assign(data.begin() + 8, data.begin() + 12);
break;
default:
return false;
}
return true;
}
std::string getName() const override { return "ModbusTCP"; }
uint16_t getDefaultPort() const override { return 502; }
void setDeviceId(uint8_t id) { device_id = id; }
private:
uint8_t device_id = 1; // 默认设备地址
};
// ====================== CANopen over TCP 协议实现 ======================
class CANopenTCPProtocol : public IProtocol {
public:
std::vector<uint8_t> serializeRequest(
const std::string& command,
const std::vector<uint8_t>& params) override {
// CANopen over TCP 报文结构: [帧类型(1) | 长度(2) | CAN ID(4) | 数据...]
std::vector<uint8_t> packet;
if (command == "SDO_WRITE") {
// SDO写请求
packet.push_back(0x01); // 帧类型: SDO请求
// 参数格式: [索引(2) | 子索引(1) | 数据...]
if (params.size() < 3) {
throw std::invalid_argument("Invalid parameters for SDO_WRITE");
}
uint16_t length = static_cast<uint16_t>(4 + params.size()); // CAN ID(4) + 数据
packet.push_back(length >> 8);
packet.push_back(length & 0xFF);
// CAN ID (COB-ID)
uint32_t can_id = 0x600 + node_id; // 客户端->服务器 SDO
for (int i = 0; i < 4; i++) {
packet.push_back((can_id >> (8*i)) & 0xFF);
}
// SDO命令字: 写请求 (0x23 = 写入4字节, 0x2B = 写入2字节等)
uint8_t command_byte = getSdoCommandByte(params.size() - 3);
packet.push_back(command_byte);
packet.push_back(params[0]); // 索引低字节
packet.push_back(params[1]); // 索引高字节
packet.push_back(params[2]); // 子索引
// SDO数据
packet.insert(packet.end(), params.begin() + 3, params.end());
} else if (command == "PDO_SEND") {
// PDO传输
packet.push_back(0x02); // 帧类型: PDO
uint16_t length = static_cast<uint16_t>(4 + params.size());
packet.push_back(length >> 8);
packet.push_back(length & 0xFF);
// CAN ID (PDO COB-ID)
uint32_t can_id = 0x180 + node_id; // PDO1发送
for (int i = 0; i < 4; i++) {
packet.push_back((can_id >> (8*i)) & 0xFF);
}
// PDO数据
packet.insert(packet.end(), params.begin(), params.end());
} else {
throw std::invalid_argument("Unsupported CANopen command: " + command);
}
return packet;
}
bool parseResponse(const std::vector<uint8_t>& data, std::vector<uint8_t>& result) override {
if (data.size() < 3) return false;
uint8_t frame_type = data[0];
uint16_t length = (static_cast<uint16_t>(data[1]) << 8) | data[2];
// 检查长度一致性
if (data.size() < static_cast<size_t>(3 + length)) return false;
switch (frame_type) {
case 0x01: // SDO响应
// 数据格式: [CAN ID(4) | 命令字(1) | 索引(2) | 子索引(1) | 数据...]
if (length < 8) return false;
result.assign(data.begin() + 7, data.begin() + 3 + length);
return (data[7] & 0x80) == 0; // 最高位为0表示成功
case 0x02: // PDO接收
result.assign(data.begin() + 7, data.begin() + 3 + length);
return true;
default:
return false;
}
}
std::string getName() const override { return "CANopenTCP"; }
uint16_t getDefaultPort() const override { return 1202; }
void setNodeId(uint8_t id) { node_id = id; }
private:
uint8_t node_id = 1; // CANopen节点ID
uint8_t getSdoCommandByte(size_t data_size) {
switch (data_size) {
case 1: return 0x2F; // 写入1字节
case 2: return 0x2B; // 写入2字节
case 3: return 0x27; // 写入3字节
case 4: return 0x23; // 写入4字节
default: throw std::invalid_argument("Unsupported SDO data size");
}
}
};
// ====================== 自定义二进制协议实现 ======================
class BinaryProtocol : public IProtocol {
public:
std::vector<uint8_t> serializeRequest(
const std::string& command,
const std::vector<uint8_t>& params) override {
// 协议格式: [起始符(1)|命令ID(1)|数据长度(2)|数据...|CRC16(2)]
std::vector<uint8_t> packet;
// 起始符
packet.push_back(0xAA);
// 命令ID映射
static const std::map<std::string, uint8_t> command_map = {
{"READ_STATUS", 0x01},
{"SET_PARAM", 0x02},
{"START_PROCESS", 0x03}
};
auto it = command_map.find(command);
if (it == command_map.end()) {
throw std::invalid_argument("Unknown command: " + command);
}
packet.push_back(it->second);
// 数据长度
uint16_t data_length = static_cast<uint16_t>(params.size());
packet.push_back(data_length >> 8);
packet.push_back(data_length & 0xFF);
// 数据
packet.insert(packet.end(), params.begin(), params.end());
// 计算CRC
uint16_t crc = calculateCRC(packet);
packet.push_back(crc >> 8);
packet.push_back(crc & 0xFF);
return packet;
}
bool parseResponse(const std::vector<uint8_t>& data, std::vector<uint8_t>& result) override {
// 最小响应长度: 起始符(1)+状态(1)+长度(2)+CRC(2) = 6字节
if (data.size() < 6) return false;
// 检查起始符
if (data[0] != 0xAA) return false;
// 获取状态码
uint8_t status = data[1];
// 获取数据长度
uint16_t length = (static_cast<uint16_t>(data[2]) << 8) | data[3];
// 检查长度一致性 (数据 + 状态 + 长度字段 + CRC = 4 + length)
if (data.size() != static_cast<size_t>(6 + length)) return false;
// 校验CRC
uint16_t received_crc = (static_cast<uint16_t>(data[data.size()-2]) << 8) | data[data.size()-1];
uint16_t calculated_crc = calculateCRC({data.begin(), data.end()-2});
if (received_crc != calculated_crc) return false;
// 提取响应数据
if (length > 0) {
result.assign(data.begin() + 4, data.end() - 2);
}
// 状态码0表示成功
return status == 0;
}
std::string getName() const override { return "BinaryProtocol"; }
uint16_t getDefaultPort() const override { return 8080; }
private:
uint16_t calculateCRC(const std::vector<uint8_t>& data) {
uint16_t crc = 0xFFFF;
for (uint8_t byte : data) {
crc ^= static_cast<uint16_t>(byte) << 8;
for (int i = 0; i < 8; i++) {
crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : (crc << 1);
}
}
return crc;
}
};
// ====================== Socket 通信管理器 ======================
class SocketCommManager {
public:
SocketCommManager() {
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
throw std::runtime_error("WSAStartup failed");
}
#endif
}
~SocketCommManager() {
disconnect();
#ifdef _WIN32
WSACleanup();
#endif
}
// 连接到模块
bool connect(const std::string& ip, uint16_t port = 0, ProtocolType proto = ProtocolType::CUSTOM) {
disconnect();
// 获取默认端口(如果未指定)
if (port == 0) {
port = getProtocol(proto)->getDefaultPort();
}
// 创建Socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
return false;
}
// 配置服务器地址
sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
if (inet_pton(AF_INET, ip.c_str(), &server_addr.sin_addr) <= 0) {
disconnect();
return false;
}
// 设置超时
setSocketTimeout(sock, 3000);
// 建立连接
if (::connect(sock, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
disconnect();
return false;
}
current_protocol = proto;
return true;
}
// 断开连接
void disconnect() {
if (sock != INVALID_SOCKET) {
CLOSE_SOCKET(sock);
sock = INVALID_SOCKET;
}
}
// 发送命令
bool sendCommand(const std::string& command, const std::vector<uint8_t>& params) {
if (sock == INVALID_SOCKET) return false;
try {
// 序列化请求
auto protocol = getProtocol(current_protocol);
auto packet = protocol->serializeRequest(command, params);
// 发送数据
size_t total_sent = 0;
while (total_sent < packet.size()) {
int sent = ::send(sock, reinterpret_cast<const char*>(packet.data() + total_sent),
packet.size() - total_sent, 0);
if (sent <= 0) {
disconnect();
return false;
}
total_sent += sent;
}
return true;
} catch (const std::exception& e) {
std::cerr << "Protocol error: " << e.what() << std::endl;
return false;
}
}
// 接收响应
bool receiveResponse(std::vector<uint8_t>& result, size_t timeout = 3000) {
if (sock == INVALID_SOCKET) return false;
auto protocol = getProtocol(current_protocol);
std::vector<uint8_t> response;
// 接收数据(简单实现,实际应该根据协议特性优化)
uint8_t buffer[1024];
int received = recv(sock, reinterpret_cast<char*>(buffer), sizeof(buffer), 0);
if (received <= 0) {
disconnect();
return false;
}
response.assign(buffer, buffer + received);
// 解析响应
return protocol->parseResponse(response, result);
}
// 执行命令(发送+接收)
bool executeCommand(const std::string& command,
const std::vector<uint8_t>& params,
std::vector<uint8_t>& result) {
return sendCommand(command, params) && receiveResponse(result);
}
// 设置协议参数
void setProtocolParam(ProtocolType proto, uint8_t id) {
auto protocol = std