java使用socket + http/ip 协议 实现与modbus二进制通信(byte)客户端

本文介绍了如何使用Java通过Socket和HTTP/IP协议来实现与Modbus设备的二进制通信。客户端接口设定包括127.0.0.1作为IP地址,502作为端口号,并且通信参数采用byte类型。

做modbus的通信客户端接口,需要双方约定:

1、协议:http/ip

2、ip、端口 :127.0.0.1    、502

3、参数条件:byte类型的参数条件

 

具体实现代码:

package com.chuang.service.impl;

import com.chuang.dao.TestTableDao;
import com.chuang.entity.ModbusCondition;
import com.chuang.entity.NextPosition;
import com.chuang.entity.TestTableEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
@EnableScheduling
public class TimingTask {

    @Autowired
    TestTableDao testDao;


    @Scheduled(cron = "0 0/5 * * * ?")  //spring定时任务:周期5分钟
//    @Scheduled(cron = "0/5 * * * * ?")
    public void timeTskByModbusInsert(){
        modbusPubMethd();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        System.out.println(df.format(new Date()));// new Date()为获取当前系统时间
    }

    public void modbusPubMethd(){
        long startTime=System.currentTimeMillis();
        TestTableEntity tableEntity = new TestTabl
<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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值