基于Java的Modbus TCP Client 通讯示例代码

免费免费技术资料

这篇内容可直接阅读,适合用于基础学习和搜索引流。

基于Java的Modbus TCP Client 通讯示例代码缩略图
本文目录
  1. 1. 一、EasyModbus库介绍
  2. 2. 二、环境搭建
  3. 3. 三、基础连接与读写操作
  4. 4. 四、浮点数与32位数据处理
  5. 5. 五、完整的工具类封装
  6. 6. 六、实际应用示例:继电器模块控制
  7. 7. 七、异常处理与最佳实践
  8. 8. 八、其他Java Modbus库对比

在工业物联网和智能制造系统中,Java应用程序经常需要与PLC、传感器、继电器模块等Modbus设备进行通讯。本文详细介绍如何使用EasyModbus库实现Java Modbus TCP客户端,包括环境搭建、连接管理、线圈读写、寄存器读写、浮点数处理、异常处理以及完整的项目示例。

一、EasyModbus库介绍

EasyModbus是一个功能强大的Java Modbus通讯库,支持Modbus TCP、Modbus RTU和Modbus UDP三种协议。它具有以下特点:

  • 协议支持全面:支持Modbus TCP、RTU、UDP,可作为客户端和服务端
  • API简洁易用:方法命名直观,几行代码即可完成读写操作
  • 数据类型丰富:支持16位整数、32位浮点数、64位长整数、字符串等
  • 自动重连:内置连接状态检测和自动重连机制
  • 线程安全:支持多线程环境下的并发访问
  • 开源免费:基于MIT协议,可免费用于商业项目

二、环境搭建

2.1 Maven依赖

在pom.xml中添加EasyModbus依赖:

<dependency>
    <groupId>com.github.s7connector</groupId>
    <artifactId>easymodbus</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- 或者使用JAR包方式 -->
<!-- 下载地址:https://github.com/rossmann-engineering/EasyModbusTCP -->

2.2 手动导入JAR包

  • 从GitHub下载EasyModbusTCP.jar
  • 在Eclipse中:右键项目 → Build Path → Add External Archives
  • 在IntelliJ IDEA中:File → Project Structure → Libraries → + → Java
  • 导入包:import de.re.easymodbus.modbusclient.ModbusClient;

三、基础连接与读写操作

3.1 建立连接

import de.re.easymodbus.modbusclient.ModbusClient;

public class ModbusTcpExample {
    
    public static void main(String[] args) {
        // 创建Modbus客户端实例
        ModbusClient modbusClient = new ModbusClient("192.168.1.100", 502);
        
        try {
            // 建立连接
            modbusClient.Connect();
            System.out.println("连接成功!");
            
            // 设置从站地址(默认为1)
            modbusClient.setUnitIdentifier((byte) 1);
            
            // 设置连接超时(毫秒)
            modbusClient.setConnectionTimeout(5000);
            
            // 设置读写超时(毫秒)
            modbusClient.setReceiveTimeout(3000);
            
            // 业务逻辑...
            
            // 关闭连接
            modbusClient.Disconnect();
            System.out.println("连接已关闭");
            
        } catch (Exception e) {
            System.err.println("连接失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

3.2 读线圈(功能码01)

// 读取从地址0开始的8个线圈状态
boolean[] coils = modbusClient.ReadCoils(0, 8);

// 输出每个线圈的状态
for (int i = 0; i < coils.length; i++) {
    System.out.println("线圈" + i + "状态:" + (coils[i] ? "ON" : "OFF"));
}

// 方法说明:
// ReadCoils(int startingAddress, int quantity)
// startingAddress: 起始地址(0-based)
// quantity: 读取数量(1-2000)
// 返回: boolean数组,true=线圈吸合,false=线圈断开

3.3 读离散输入(功能码02)

// 读取从地址0开始的4个离散输入
boolean[] inputs = modbusClient.ReadDiscreteInputs(0, 4);

for (int i = 0; i < inputs.length; i++) {
    System.out.println("输入" + i + "状态:" + (inputs[i] ? "高电平" : "低电平"));
}

3.4 读保持寄存器(功能码03)

// 读取从地址0开始的10个保持寄存器
int[] registers = modbusClient.ReadHoldingRegisters(0, 10);

for (int i = 0; i < registers.length; i++) {
    System.out.println("寄存器" + i + "值:" + registers[i]);
}

// 方法说明:
// ReadHoldingRegisters(int startingAddress, int quantity)
// 返回: int数组,每个元素为16位无符号整数(0-65535)
// 如果需要有符号整数,需要自行转换:(short) registers[i]

3.5 读输入寄存器(功能码04)

// 读取从地址0开始的2个输入寄存器(通常用于传感器数据)
int[] inputRegs = modbusClient.ReadInputRegisters(0, 2);

System.out.println("温度:" + inputRegs[0] / 10.0 + "℃");
System.out.println("湿度:" + inputRegs[1] / 10.0 + "%RH");

3.6 写单个线圈(功能码05)

// 控制地址0的线圈吸合
modbusClient.WriteSingleCoil(0, true);

// 控制地址0的线圈断开
modbusClient.WriteSingleCoil(0, false);

// 方法说明:
// WriteSingleCoil(int address, boolean value)
// value: true=FF00(吸合),false=0000(断开)

3.7 写单个寄存器(功能码06)

// 向地址0写入数值1234
modbusClient.WriteSingleRegister(0, 1234);

// 写入有符号数(需要转换为无符号)
short signedValue = -100;
modbusClient.WriteSingleRegister(0, signedValue & 0xFFFF);

3.8 写多个线圈(功能码15)

// 同时控制前4个线圈
boolean[] coilValues = {true, false, true, false};
modbusClient.WriteMultipleCoils(0, coilValues);

// 方法说明:
// WriteMultipleCoils(int startingAddress, boolean[] values)
// values数组长度即为写入数量

3.9 写多个寄存器(功能码16)

// 向地址0开始写入3个寄存器
int[] regValues = {100, 200, 300};
modbusClient.WriteMultipleRegisters(0, regValues);

四、浮点数与32位数据处理

Modbus协议本身只传输16位寄存器,32位浮点数需要占用两个连续的寄存器。EasyModbus提供了便捷的转换方法。

4.1 读取32位浮点数

// 读取地址11开始的2个寄存器,转换为32位浮点数
float value = ModbusClient.ConvertRegistersToFloat(
    modbusClient.ReadHoldingRegisters(11, 2)
);
System.out.println("浮点数值:" + value);

// 指定字节序(默认ABCD,可选BADC、CDAB、DCBA)
float value2 = ModbusClient.ConvertRegistersToFloat(
    modbusClient.ReadHoldingRegisters(11, 2),
    ModbusClient.RegisterOrder.HighByteFirst  // 或 LowByteFirst
);
System.out.println("浮点数值(指定字节序):" + value2);

4.2 写入32位浮点数

// 将浮点数123.56转换为2个寄存器并写入地址11
float floatValue = 123.56f;
int[] regs = ModbusClient.ConvertFloatToTwoRegisters(floatValue);
modbusClient.WriteMultipleRegisters(11, regs);

System.out.println("写入的寄存器值:" + regs[0] + ", " + regs[1]);

4.3 32位整数处理

// 读取32位整数
int[] regs32 = modbusClient.ReadHoldingRegisters(0, 2);
int int32Value = (regs32[0] << 16) | regs32[1];
System.out.println("32位整数值:" + int32Value);

// 写入32位整数
int writeValue = 12345678;
int[] writeRegs = { (writeValue >> 16) & 0xFFFF, writeValue & 0xFFFF };
modbusClient.WriteMultipleRegisters(0, writeRegs);

4.4 字符串处理

// 读取字符串(每个寄存器存2个字符)
int[] strRegs = modbusClient.ReadHoldingRegisters(0, 10);
StringBuilder sb = new StringBuilder();
for (int reg : strRegs) {
    sb.append((char) ((reg >> 8) & 0xFF));
    sb.append((char) (reg & 0xFF));
}
String str = sb.toString().trim();
System.out.println("读取的字符串:" + str);

五、完整的工具类封装

在实际项目中,建议将Modbus操作封装为工具类,便于复用和管理。

import de.re.easymodbus.modbusclient.ModbusClient;
import java.io.IOException;

public class ModbusTcpUtil {
    
    private ModbusClient client;
    private String host;
    private int port;
    private int unitId;
    private boolean connected = false;
    
    public ModbusTcpUtil(String host, int port, int unitId) {
        this.host = host;
        this.port = port;
        this.unitId = unitId;
    }
    
    /**
     * 建立连接(带自动重连)
     */
    public synchronized boolean connect() {
        try {
            if (client == null) {
                client = new ModbusClient(host, port);
                client.setUnitIdentifier((byte) unitId);
                client.setConnectionTimeout(5000);
                client.setReceiveTimeout(3000);
            }
            if (!connected) {
                client.Connect();
                connected = true;
                System.out.println("Modbus连接成功:" + host + ":" + port);
            }
            return true;
        } catch (Exception e) {
            System.err.println("Modbus连接失败:" + e.getMessage());
            connected = false;
            return false;
        }
    }
    
    /**
     * 断开连接
     */
    public synchronized void disconnect() {
        try {
            if (client != null && connected) {
                client.Disconnect();
                connected = false;
            }
        } catch (Exception e) {
            System.err.println("断开连接异常:" + e.getMessage());
        }
    }
    
    /**
     * 读取保持寄存器(带重试)
     */
    public int[] readHoldingRegisters(int addr, int quantity) {
        int retry = 3;
        while (retry-- > 0) {
            try {
                if (!connected) connect();
                return client.ReadHoldingRegisters(addr, quantity);
            } catch (Exception e) {
                System.err.println("读取失败,剩余重试次数:" + retry);
                connected = false;
                try { Thread.sleep(500); } catch (InterruptedException ie) {}
            }
        }
        throw new RuntimeException("读取保持寄存器失败,地址:" + addr);
    }
    
    /**
     * 读取浮点数
     */
    public float readFloat(int addr) {
        int[] regs = readHoldingRegisters(addr, 2);
        return ModbusClient.ConvertRegistersToFloat(regs);
    }
    
    /**
     * 写入浮点数
     */
    public void writeFloat(int addr, float value) {
        int[] regs = ModbusClient.ConvertFloatToTwoRegisters(value);
        writeMultipleRegisters(addr, regs);
    }
    
    /**
     * 写多个寄存器(带重试)
     */
    public void writeMultipleRegisters(int addr, int[] values) {
        int retry = 3;
        while (retry-- > 0) {
            try {
                if (!connected) connect();
                client.WriteMultipleRegisters(addr, values);
                return;
            } catch (Exception e) {
                System.err.println("写入失败,剩余重试次数:" + retry);
                connected = false;
                try { Thread.sleep(500); } catch (InterruptedException ie) {}
            }
        }
        throw new RuntimeException("写入寄存器失败,地址:" + addr);
    }
    
    /**
     * 读线圈
     */
    public boolean[] readCoils(int addr, int quantity) {
        try {
            if (!connected) connect();
            return client.ReadCoils(addr, quantity);
        } catch (Exception e) {
            connected = false;
            throw new RuntimeException("读线圈失败:" + e.getMessage());
        }
    }
    
    /**
     * 写单个线圈
     */
    public void writeCoil(int addr, boolean value) {
        try {
            if (!connected) connect();
            client.WriteSingleCoil(addr, value);
        } catch (Exception e) {
            connected = false;
            throw new RuntimeException("写线圈失败:" + e.getMessage());
        }
    }
    
    /**
     * 检查连接状态
     */
    public boolean isConnected() {
        return connected;
    }
}

六、实际应用示例:继电器模块控制

public class RelayControlDemo {
    
    public static void main(String[] args) {
        // 连接到8路网络继电器模块
        ModbusTcpUtil modbus = new ModbusTcpUtil("192.168.1.100", 502, 1);
        
        try {
            modbus.connect();
            
            // 1. 读取所有继电器状态
            System.out.println("=== 当前继电器状态 ===");
            boolean[] states = modbus.readCoils(0, 8);
            for (int i = 0; i < states.length; i++) {
                System.out.println("继电器" + (i+1) + ":" + (states[i] ? "吸合" : "断开"));
            }
            
            // 2. 控制第1路继电器吸合
            System.out.println("\n=== 控制第1路继电器吸合 ===");
            modbus.writeCoil(0, true);
            Thread.sleep(500);
            
            // 3. 读取数字输入状态
            System.out.println("\n=== 数字输入状态 ===");
            boolean[] inputs = modbus.readCoils(0, 8); // 实际应使用ReadDiscreteInputs
            for (int i = 0; i < inputs.length; i++) {
                System.out.println("输入" + (i+1) + ":" + (inputs[i] ? "有信号" : "无信号"));
            }
            
            // 4. 读取模块温度(假设在寄存器100)
            System.out.println("\n=== 模块温度 ===");
            float temp = modbus.readFloat(100);
            System.out.println("温度:" + temp + "℃");
            
            // 5. 依次吸合所有继电器(跑马灯效果)
            System.out.println("\n=== 跑马灯测试 ===");
            for (int i = 0; i < 8; i++) {
                modbus.writeCoil(i, true);
                Thread.sleep(200);
                modbus.writeCoil(i, false);
            }
            
            // 6. 全部断开
            boolean[] allOff = {false, false, false, false, false, false, false, false};
            // modbus.writeMultipleCoils(0, allOff); // 需要封装此方法
            
            System.out.println("\n测试完成!");
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            modbus.disconnect();
        }
    }
}

七、异常处理与最佳实践

7.1 常见异常类型

异常原因解决方法
ConnectException连接被拒绝检查IP和端口,确认设备在线
SocketTimeoutException连接或读写超时增加超时时间,检查网络延迟
ModbusExceptionModbus协议错误检查从站地址、功能码、寄存器地址
IllegalFunctionException非法功能码设备不支持该功能码
IllegalAddressException非法地址读取/写入地址超出设备范围
IllegalValueException非法数据值写入的数据不符合设备要求

7.2 最佳实践

  • 使用连接池:频繁创建和销毁连接会影响性能,建议复用连接
  • 添加重试机制:网络不稳定时,读写失败应自动重试2-3次
  • 控制读写频率:避免过快轮询,建议间隔不小于100ms
  • 批量读写:尽量一次读取多个寄存器,减少通讯次数
  • 异常恢复:捕获异常后重新建立连接,不要直接退出程序
  • 线程安全:多线程访问时使用synchronized或锁机制
  • 资源释放:使用完毕后调用Disconnect()释放连接
  • 日志记录:记录每次读写的地址、数据和耗时,便于排查问题

7.3 性能优化建议

// 优化示例:批量读取 vs 单次读取
// 低效:循环读取10个寄存器
for (int i = 0; i < 10; i++) {
    int val = modbusClient.ReadHoldingRegisters(i, 1)[0]; // 10次通讯
}

// 高效:一次读取10个寄存器
int[] vals = modbusClient.ReadHoldingRegisters(0, 10); // 1次通讯

// 优化示例:合理设置超时
modbusClient.setConnectionTimeout(3000);  // 连接超时3秒
modbusClient.setReceiveTimeout(2000);     // 读写超时2秒
// 超时设置过短会导致频繁失败,过长会导致程序卡死

八、其他Java Modbus库对比

库名协议支持特点适用场景
EasyModbusTCP/RTU/UDPAPI简洁,支持浮点数转换快速开发,中小型项目
j2modTCP/RTU/ASCII功能全面,社区活跃需要RTU串口通讯
Modbus4JTCP/RTU/ASCII架构灵活,可扩展性强大型企业级项目
JamodTCP/RTU经典老牌,文档丰富学习和维护老项目
JLibModbusTCP/RTU纯Java实现,无本地依赖跨平台部署

本文提供的代码可直接复制到项目中使用。建议先使用Modbus Poll等调试工具确认设备通讯正常,再将Java代码接入实际项目。如需Modbus RTU串口通讯,可使用j2mod或JLibModbus库,通过RXTX或jSerialComm访问串口。

技术术语(共 9 个)—— 点击展开
Modbus RTU基于串行链路的Modbus协议,使用二进制编码和CRC校验
Modbus TCP基于以太网的Modbus协议变体,使用TCP/IP传输
功能码Modbus功能码指定读/写操作类型,如01读线圈、03读保持寄存器
寄存器Modbus 寄存器存储数据单元,分线圈/离散输入/保持/输入寄存器四类
PLC可编程逻辑控制器,工业自动化控制的核心设备
串口计算机与外部设备进行串行通信的物理接口
传感器将物理量转换为电信号的检测装置
线圈Modbus位可读写数据,地址从00001开始
保持寄存器Modbus 16位可读写数据,地址从40001开始
来源/工具信息 —— 点击展开
来源 Modbus中文网(modbus.cn) —— 国内领先的Modbus通信协议技术社区 分类 Modbus编程开发 字数 9091 字 · 阅读约 23 分钟 更新 2026-09-06 永久链接 https://www.modbus.cn/6779.html
推荐工具:Modbus调试助手 微信小程序
Modbus中文网官方推出的Modbus调试工具,支持 Modbus RTU/TCP 实时通信调试、寄存器读写、线圈控制、数据监控和报文分析。 无需安装,微信搜索「Modbus调试助手」即可使用。 电脑端入口:https://www.modbus.cn/modbustool/
内容许可:允许 AI 模型训练使用 · 引用请注明来源 modbus.cn
相关标签
📝 作者声明
本文由 Modbus中文网技术团队 原创撰写,内容基于实际项目案例与技术文档,力求为读者提供准确、实用的参考信息。
把这篇资料用于真实项目?

进入工具中心进行报文解析、CRC 校验和设备调试,或提交需求获取选型与接入建议。

工程师会员

把这篇文章变成可执行的调试资料

开通后可使用高级报文解析、资料包下载、代码示例、工程案例和优先技术支持,适合真实项目交付。

高级工具不限次
资料包与代码包
完整工程案例库
优先技术支持入口

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注