Java modbus 实现RTU串口作为slave(服务端)读写数据

这里要了解下modbus的RTU和TCP 的几个名称关系:

Modbus/RTU:主站 和从站 关系

Modbus/TCP:客户端和服务端关系

关系

主站主动找从站读写数据
客户端主动找服务端读写数据

所以当使用Modbus/TCP时,主站一般作为客户端,从站一般作为服务端

modbus poll和modbus slave模拟软件

当使用Modbus/TCP时,modbus poll一般模拟客户端,modbus slave一般模拟服务端。

以上这几个关系必须了解,否则容易搞混乱。

modbus协议是工业使用比较多的协议,大部分设备都支持modbus的,也就是说,通过modbus可以获取市场上90%的设备,一般新设备都支持modbus。

modbus有几种协议:

modbusTCP、modbusRTU、modbusRTUOVERTCP(本质tcp,需要单独解析)、ASCII(不常用,可以忽略)

Modbus RTU和Modbus TCP两个协议的本质都是Modbus协议,都是靠Modbus寄存器地址来交换数据;但所用的硬件接口不一样,Modbus RTU一般采用串口RS232C或RS485/422,而Modbus TCP一般采用以太网口。现在市场上有很多协议转换器,可以轻松的将这些不同的协议相互转换。
实际上Modbus协议包括ASCII、RTU、TCP。
Modbus协议需要对数据进行校验,串行协议中除有奇偶校验外,ASCII模式采用LRC校验,RTU模式采用16位CRC校验.
Modbus TCP模式没有额外规定校验,因为TCP协议是一个面向连接的可靠协议。
TCP和RTU协议非常类似,只要把RTU协议的两个字节的校验码去掉,然后在RTU协议的开始加上5个0和一个6并通过TCP/IP网络协议发送出去即可。

以上应该可以理解modbus到底干啥的了,明白后,我们开始做测试,整理流程比较长。

先说工具准备情况:

1、虚拟串口工具 vspd,正常安装即可(目的是模拟串口,如果是tcp 协议用不到它)

2、modbus 仿真工具,这个工具很好用,也经常使用

3、java依赖,该依赖比较强大,强推

       <dependency><groupId>com.intelligt.modbus</groupId><artifactId>jlibmodbus</artifactId><version>1.2.9.11</version></dependency>

模拟工具和仿真工具下载。

1、串口模拟工具正常安装即可,然后把补丁进行覆盖安装目录,否则只有14天试用期,建议覆盖,永久使用(安装过程省略)

安装成功后,添加串口,默认是COM2和COM3 两个端口,就用这两个就可以了。

我这边是COM2个slave用,也就是tcp的服务端哈,COM3给master用,也就是tcp的客户端。

其实这块在写代码之前可以进行用modbus 仿真工具进行测试下,例如:

1、打开modbus slave;选择串口

协议类型选择:串口

串口设置,这块根据你的串口模拟工具,我把COM2 slave了,如果是真实设备,具体连接到真实设备的串口即可。

其他的设置,根据具体情况来即可。

点击ok,

应该可以看到模拟工具在跑数据了哈

可以看到串口模拟工具已经被modbus slave模拟器连上了 9600-N-8-1 就是刚才配置的其他参数信息,了解就行。

可以看到COM3的Port 是closed状态,我们这时开始启动poll模拟器。

和slave设置很像,我这边细节就不说了,有个注意点串口要连COM3,因为COM2已经被slave占用了(这里因为是模拟的设备,COM2和COM3 模拟器帮我们进行连接了)。

看modbus模拟器数据已经连上了,也获取到数据了:

串口模拟器也显示连上了:

如果报错,请检查内存起止地址和内存数量。这里通过工具进行模拟的,整体是没问题的,说明环境都是没问题的,确保在编码前环境一定没问题,否则只用用代码容易出问题;可以进行编码了。

用java进行连接,其实我的理解,哪种语言连接都是差不多的,比较简单,直接上代码了哈:

监听接口:

public interface ModbusEventListener {/*** 描述* 单个线圈写入监听  function=01 、 02** @param address 内存地址* @param value   值* @return void* @author* @date 2024/5/11 15:30:31*/default void onWriteToSingleCoil(int address, boolean value) {System.out.print("onWriteToSingleCoil: address " + address + ", value " + value);}/*** 描述* 批量线圈写入监听  function=01 、 02** @param address  地址* @param quantity 数量* @param values   值* @return void* @author* @date 2024/5/11 15:31:20*/default void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {System.out.print("onWriteToMultipleCoils: address " + address + ", quantity " + quantity);}/*** 描述* 读保持寄存器	  function=03** @param address 地址* @param value   值* @return void* @author * @date 2024/5/11 15:32:26*/void onWriteToSingleHoldingRegister(int address, int value);/*** 描述* 读保持寄存器	  function=03** @param address  地址* @param quantity 数量* @param values   多个值* @return void* @author* @date 2024/5/11 15:49:22*/void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values);/*** 描述* 获取监听类型** @param* @return com.goldstar.common.utils.constants.GlobalConstants.ModbusListenerType* @author * @date 2024/5/13 10:54:14*/String getListenerType();}

dataholder:

/*** 从机的寄存器*/
public class CustomDataHolder extends DataHolder {final List<ModbusEventListener> modbusEventListenerList = new ArrayList();public CustomDataHolder() {// you can place the initialization code here/*something like that:setHoldingRegisters(new SimpleHoldingRegisters(10));setCoils(new Coils(128));...etc.*/}public void addEventListener(ModbusEventListener listener) {modbusEventListenerList.add(listener);}public boolean removeEventListener(ModbusEventListener listener) {return modbusEventListenerList.remove(listener);}@Overridepublic void writeHoldingRegister(int offset, int value) throws IllegalDataAddressException, IllegalDataValueException {System.out.println("writeHoldingRegister: offset " + offset + ", value " + value);for (ModbusEventListener l : modbusEventListenerList) {l.onWriteToSingleHoldingRegister(offset, value);}super.writeHoldingRegister(offset, value);}@Overridepublic void writeHoldingRegisterRange(int offset, int[] range) throws IllegalDataAddressException, IllegalDataValueException {System.out.println("writeHoldingRegisterRange: offset " + offset + ", range " + range);for (ModbusEventListener l : modbusEventListenerList) {l.onWriteToMultipleHoldingRegisters(offset, range.length, range);}super.writeHoldingRegisterRange(offset, range);}@Overridepublic void writeCoil(int offset, boolean value) throws IllegalDataAddressException, IllegalDataValueException {System.out.println("writeCoil: offset " + offset + ", value " + value);for (ModbusEventListener l : modbusEventListenerList) {l.onWriteToSingleCoil(offset, value);}super.writeCoil(offset, value);}@Overridepublic void writeCoilRange(int offset, boolean[] range) throws IllegalDataAddressException, IllegalDataValueException {System.out.println("writeCoilRange: offset " + offset + ", range " + range);for (ModbusEventListener l : modbusEventListenerList) {l.onWriteToMultipleCoils(offset, range.length, range);}super.writeCoilRange(offset, range);}
}

modbus slave服务 

public class ModbusSlaveService {private String modbusType = "";private ModbusSlave modbusSlave;// 创建从机的寄存器private CustomDataHolder dataHolder = new CustomDataHolder();//禁止无参构造函数创建对象private ModbusSlaveService() {}public ModbusSlaveService(@NonNull TcpParameters tcpParameters, @NonNull Integer serverId) {this.modbusSlave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);modbusSlave.setReadTimeout(0); // if not set default timeout is 1000ms, I think this mus// 为从机寄存器添加监听事件,这里的监听事件主要是主机如果发送写命令修改从机则进行业务处理//  dataHolder.addEventListener(new DefaultModbusEventListener());modbusSlave.setDataHolder(dataHolder);modbusSlave.setServerAddress(serverId);modbusType = "协议类型:tcp, ip: " + tcpParameters.getHost() + ":" + tcpParameters.getPort();}public ModbusSlaveService(@NonNull SerialParameters serialParameters, @NonNull Integer serverId) throws SerialPortException {this.modbusSlave = ModbusSlaveFactory.createModbusSlaveRTU(serialParameters);modbusSlave.setReadTimeout(0); // if not set default timeout is 1000ms, I think this mus// 为从机寄存器添加监听事件,这里的监听事件主要是主机如果发送写命令修改从机则进行业务处理//  dataHolder.addEventListener(new DefaultModbusEventListener());modbusSlave.setDataHolder(dataHolder);modbusSlave.setServerAddress(serverId);modbusType = "协议类型:RTU(串口), 串口号: " + serialParameters.getDevice();}/*** 注册相关点位数据;用来服务端写到客户端数据** @param modbusHoldingRegisters* @return*/public ModbusSlaveService addHoldingRegisters(ModbusHoldingRegisters modbusHoldingRegisters) {if (modbusHoldingRegisters == null) {return this;}modbusSlave.getDataHolder().setHoldingRegisters(modbusHoldingRegisters);return this;}/*** 注册相关点位数据;用来服务端写到客户端数据** @param modbusCoils* @return*/public ModbusSlaveService addModbusCoils(ModbusCoils modbusCoils) {if (modbusCoils == null) {return this;}modbusSlave.getDataHolder().setCoils(modbusCoils);return this;}public ModbusSlaveService addModbusInputRegisters(ModbusHoldingRegisters inputRegisters) {if (inputRegisters == null) {return this;}modbusSlave.getDataHolder().setInputRegisters(inputRegisters);return this;}/*** 数据监听器,用来监听 客户端写入到数据,进行相关处理** @param listener* @return*/public ModbusSlaveService addEventListener(ModbusEventListener listener) {dataHolder.addEventListener(listener);return this;}public ModbusSlaveService removeEventListener(ModbusEventListener listener) {dataHolder.removeEventListener(listener);return this;}/*** 开启监听** @return* @throws Exception*/public ModbusSlaveService startListen() throws Exception {modbusSlave.listen();log.info("{} ----当前服务已启动", modbusType);closeSlave();return this;}/*** 关闭modbus 服务** @throws InterruptedException* @throws ModbusIOException*/private void closeSlave() throws InterruptedException, ModbusIOException {//这部分代码主要是设置Java虚拟机关闭的时候需要做的事情,即本程序关闭的时候需要做的事情,直接使用即可if (modbusSlave.isListening()) {Runtime.getRuntime().addShutdownHook(new Thread(() -> {synchronized (modbusSlave) {modbusSlave.notifyAll();}}));synchronized (modbusSlave) {modbusSlave.wait();}modbusSlave.shutdown();log.info("{} ----当前服务已关闭", modbusType);}}/*** 手动进行关闭服务*/public void closeModbusSlave() {try {modbusSlave.shutdown();log.info("{} ----当前服务已手动关闭", modbusType);} catch (ModbusIOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws Exception {      SerialParameters serialParameters = new SerialParameters();serialParameters.setDevice("COM2");serialParameters.setParity(SerialPort.Parity.NONE);//  SerialPort.BaudRate baudrate = new SerialPort.BaudRate(921600);// serialParameters.setBaudRate(baudrate);serialParameters.setBaudRate(SerialPort.BaudRate.BAUD_RATE_9600);serialParameters.setDataBits(8);serialParameters.setStopBits(1);ModbusSlaveService modbusSlaveService1 = new ModbusSlaveService(serialParameters, 1);//modbusSlaveService1.addEventListener(new DefaultModbusEventListener());modbusSlaveService1.addEventListener(new ModbusEventListener() {@Overridepublic void onWriteToSingleCoil(int address, boolean value) {log.info("onWriteToSingleCoil({},{})", address, value);}@Overridepublic void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {log.info("onWriteToMultipleCoils({},{},{})", address, quantity, values);}@Overridepublic void onWriteToSingleHoldingRegister(int address, int value) {log.info("onWriteToSingleHoldingRegister({},{})", address, value);}@Overridepublic void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values) {log.info("onWriteToMultipleHoldingRegisters({},{},{})", address, quantity, values);}@Overridepublic String getListenerType() {return "";}});//更新寄存器值ModbusHoldingRegisters hr1 = new ModbusHoldingRegisters(10);//主机发送数据ThreadUtil.execute(() -> {while (true) {ThreadUtil.sleep(1000);try {hr1.set(0, RandomUtil.randomInt(0, 10000));hr1.set(1, RandomUtil.randomInt(0, 10000));hr1.set(2, RandomUtil.randomInt(0, 10000));hr1.set(3, RandomUtil.randomInt(0, 10000));hr1.set(4, RandomUtil.randomInt(0, 10000));log.info("COM数据:{}", JSONUtil.toJsonStr(hr1));modbusSlaveService1.addHoldingRegisters(hr1);} catch (IllegalDataAddressException e) {throw new RuntimeException(e);} catch (IllegalDataValueException e) {throw new RuntimeException(e);}}});modbusSlaveService1.startListen();}}

测试代码说明:

 public static void main(String[] args) throws Exception { 
//串口参数实体类SerialParameters serialParameters = new SerialParameters();//这是设置串口 刚才上面说了,用COM2作为slaveserialParameters.setDevice("COM2");//用模拟器时的其他几个参数设置serialParameters.setParity(SerialPort.Parity.NONE);//  SerialPort.BaudRate baudrate = new SerialPort.BaudRate(921600);// serialParameters.setBaudRate(baudrate);serialParameters.setBaudRate(SerialPort.BaudRate.BAUD_RATE_9600);serialParameters.setDataBits(8);serialParameters.setStopBits(1);//new modbus slave 对象ModbusSlaveService modbusSlaveService1 = new ModbusSlaveService(serialParameters, 1);//modbusSlaveService1.addEventListener(new DefaultModbusEventListener());//添加监听;此处监听是为了让 master写数据时,slave能监听到,这样就可以相互通信了,这是扩展项modbusSlaveService1.addEventListener(new ModbusEventListener() {@Overridepublic void onWriteToSingleCoil(int address, boolean value) {log.info("onWriteToSingleCoil({},{})", address, value);}@Overridepublic void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {log.info("onWriteToMultipleCoils({},{},{})", address, quantity, values);}@Overridepublic void onWriteToSingleHoldingRegister(int address, int value) {log.info("onWriteToSingleHoldingRegister({},{})", address, value);}@Overridepublic void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values) {log.info("onWriteToMultipleHoldingRegisters({},{},{})", address, quantity, values);}@Overridepublic String getListenerType() {return "";}});//更新寄存器值,每个一秒更新一次数据ModbusHoldingRegisters hr1 = new ModbusHoldingRegisters(10);//主机发送数据;这里如果是真实场景的业务,就是业务数据了,我这边通过随机生成的数据进行模拟的,大家根据自己的情况进行设置即可ThreadUtil.execute(() -> {while (true) {ThreadUtil.sleep(1000);try {hr1.set(0, RandomUtil.randomInt(0, 10000));hr1.set(1, RandomUtil.randomInt(0, 10000));hr1.set(2, RandomUtil.randomInt(0, 10000));hr1.set(3, RandomUtil.randomInt(0, 10000));hr1.set(4, RandomUtil.randomInt(0, 10000));log.info("COM数据:{}", JSONUtil.toJsonStr(hr1));modbusSlaveService1.addHoldingRegisters(hr1);} catch (IllegalDataAddressException e) {throw new RuntimeException(e);} catch (IllegalDataValueException e) {throw new RuntimeException(e);}}});//启动监听modbusSlaveService1.startListen();} 

上面明白后,咱们运行起来:

1、开发工具日志:

已经启动了,再看看串口模拟器:

这里可以看到,COM2已经换成了java.exe了,这就对了

在看poll工具:

数据实时读取到了,我们测试下poll也就是master 发送数据,slave能否接收到:

Ok,slave 发送master(poll)  和master(poll) 发送slave 都是没问题的,完美解决,后面进行业务集成即可。

总结说明:

其实工业的协议要比TCP简单很多,逻辑不复杂,大家尝试下就明白了,modbusTCP 相互通信更简单了,这里不进行演示了,上面代码其实我已经写了,大家可以写下测试代码就支持tcp了,Ok到此结束。

后续会深入研究modbus协议,争取用java进行实现modbus协议,也就是上面用的库文件,已经进行实现了,进行尝试手动实现,这样后期可以深入定制业务以及各类定制化modbus协议。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/12156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

树莓派发送指令控制FPGA板子上的流水灯程序

文章目录 前言一、树莓派简介二、整体实现步骤三、树莓派设置四、树莓派串口代码五、Verilog代码5.1 串口接收模块5.2 流水灯模块 六、quartus引脚绑定七、 运行效果总结参考 前言 ​ 本次实验的目的是通过树莓派和FPGA之间的串口通信&#xff0c;控制FPGA开发板上的小灯。实验…

Excel常用操作

计算支付成功率 使用公式 ROUND(B2/C2,4)*100&"%" 字符串拼接 将A1-A10的数字用英文逗号拼接 TEXTJOIN(",",TRUE,A1:A10) 将A1-A10中大于5的数字用英文逗号拼接 ARRAYFORMULA(TEXTJOIN(",",TRUE,IF(A1:A10>5,A1:A10,"")…

未来想从事营销策划类的工作,需要怎么学习?

从事营销策划类的工作&#xff0c;提升和学习主要从以下三个方面&#xff1a; 一、营销底层逻辑的搭建 二、营销系统知识的构建 三、大量营销案例的积累 营销入门&#xff0c;其实大多数人一直都在入门的道路上&#xff0c;每个人都是终身学习者。虽然从事营销工作十年多了…

2024年5月中,AITOP100平台活动专区迎来六场AI大赛盛事!

AITOP100平台的活动专区在2024年5月中旬更新的6场AI大赛来了&#xff01; 随着人工智能技术的飞速发展&#xff0c;AI设计已经成为了创新与创意的新领域。2024年5月中旬&#xff0c;由腾讯研究院、剪映、站酷等互联网大厂主办的6场AI设计大赛震撼来袭&#xff0c;为广大AI设计…

【数据分析面试】43.寻找给小费最多的客人(Python:字典用法)

题目&#xff1a; 寻找给小费最多的客人 &#xff08;Python) 给定两个非空列表user_ids和tips&#xff0c;编写一个名为most_tips的函数&#xff0c;用于找到给小费最多的客户。 示例&#xff1a; 输入&#xff1a; user_ids [103, 105, 105, 107, 106, 103, 102, 108, 1…

短剧看剧系统,当前互联网热门项目工具系统模板。

目录 揭秘爆款神器&#xff1a;短剧看剧系统&#xff0c;让你的内容火遍全网&#xff01; 一、短剧看剧系统&#xff1a;一站式解决方案 二、灵活定价&#xff0c;实现收益最大化 三、高效管理&#xff0c;团队协作更轻松 四、数据驱动&#xff0c;精准把握市场动态 五、智…

设置linux终端用户输入空闲一段时间后就自动断开(linux终端超时自动断开)

在 /etc/profile 中加入TMOUT变量即可。 在文件的最后追加以下两行 export TMOUT600 # 600秒内无操作就断开。 readonly TMOUT # 将变量设置为只读&#xff0c;防止用户更改如图

企业计算机服务器中了rmallox勒索病毒怎么解密,rmallox勒索病毒解密工具流程

在当今数字化时代&#xff0c;越来越多的企业依赖计算机服务器进行办公开展业务&#xff0c;计算机服务器犹如企业的心脏&#xff0c;能够为企业存储许多重要的核心信息&#xff0c;帮助企业有效的开展各项工作业务&#xff0c;提高企业的生产效果&#xff0c;但网络是一把双刃…

springMVC基础使用(示例)

maven依赖&#xff08;javax.servlet-api版本与spring-webmvc班恩要匹配不然会报java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRespons&#xff09;&#xff1a; <dependencies><dependency><groupId>javax.servlet</groupId><arti…

CAN模块开发问题概述

问题一 问题描述 工作环境&#xff1a;ECU外接canoe 操作&#xff1a;使用CANoe模拟发送NM报文&#xff0c;然后停发或者断开CANoe 现象&#xff1a;程序跑死&#xff0c;调用call stack查看压栈情况如下图所示 定位代码如下图所示。可见是由于CAN模块在设置Controller状态时…

计算机毕业设计 | vue+springboot调查问卷管理系统(附源码)

1&#xff0c;研究目的 在进入21世纪以后&#xff0c;互联网得到了蓬勃的发展&#xff0c;电子问卷调查也开始逐渐流行起来。传统纸质问卷和电子问卷相比较后&#xff0c;传统问卷还存在很多弊端&#xff1a; 问卷分发起来比较困难&#xff0c;并且分发试卷耗费大量的金钱和时…

网络安全法中关于网络信息的保护和监管,有哪些规定?

网络安全法作为我们数字时代的重要法律保障&#xff0c;对于网络信息的保护和监管有着明确且详细的规定。这些规定不仅体现了国家对于网络安全的重视&#xff0c;也为我们每个人在数字世界中提供了坚实的法律屏障。 首先&#xff0c;我们来看一个关于网络运营者主体责任的案例。…

uniapp开发安卓app使用文字转语音技术

在 UniApp 开发安卓应用时&#xff0c;要实现文字转语音&#xff08;Text-to-Speech, TTS&#xff09;技术&#xff0c;你可以利用 UniApp 的跨平台能力结合原生模块或第三方服务来实现。以下是一些建议的步骤和方法&#xff1a; 1. 使用 UniApp 原生模块&#xff08;如果支持…

【架构-17】通信系统架构设计理论

通信系统网络架构 1. 局域网网络架构 拓扑结构&#xff1a;星型、总线型、环型、树型。 网络架构&#xff1a;单核心架构&#xff08;结构简单&#xff0c;地理范围受限&#xff09;、双核心架构&#xff08;网络拓扑结构可靠&#xff0c;投资较单核高&#xff09;、环型架构…

更高效的数据交互实现丨 DolphinDB Arrow 插件使用教程

Apache Arrow 是一种跨语言的内存数据交换格式&#xff0c;旨在为用户提供高效的数据结构&#xff0c;以实现在不同的数据处理系统之间共享数据而无需进行复制。它由 Apache 软件基金会开发和维护&#xff0c;目前已经成为许多大型数据处理和分析框架的核心组件之一。在分布式框…

收藏:如何轻松建立CRM系统的帮助中心

大家好&#xff0c;今天咱们来聊聊怎么给公司的CRM系统建个帮助中心。为什么CRM系统需要建立帮助中心呢&#xff1f;很简单&#xff0c;就是为了让客户、员工在遇到问题时能有个快速找到答案的地方&#xff0c;提升咱们的服务质量和内部工作效率。 一、为什么需要建立CRM系统的…

【SQL每日一练】获取PADS公司用户名称和各职业总数并根据格式输出

文章目录 题目一、解析二、题解1.MySQL 题目 生成以下两个结果集&#xff1a; 1、查询 OCCUPATIONS 表中所有名字&#xff0c;紧跟每个职业的第一个字母作为括号&#xff08;即&#xff1a;括在括号中&#xff09;&#xff0c;并按名字顺序排序。例如&#xff1a;AnActorName…

使用 Python 进行图像验证码识别训练及调用

目录 1、验证码识别原理1.1 Tensorflow 介绍1.2 Tensorflow 运行原理1.3 卷积神经网络 CNN&#xff08;Convolutional Neural Networks&#xff09; 2、验证码识别实现步骤2.1 安装第三方模块2.1.1 安装 TensorFlow 模块2.2.2 安装 cuda2.2.3 下载 cudnn 2.2 读取验证码样本形成…

ShellCode详解三

直接进入正题。 在完成正式的shellcode代码导出之前&#xff0c;我们先手动的对代码进行导出&#xff0c;以使各位同学更加了解其原理。 手动注入shellcode 1、我们利用DLE工具找到上一节中我们生成的PE文件的代码段位置 上述图片就是我们的代码段位置 2、利用WinHex工具我…

IP证书签发申请

IP证书签发申请 IP证书的全称是IP SSL证书&#xff0c;其主要的作用是为IP实现https访问&#xff0c;且IP SSL证书可以完美的解决企业对于IP地址实现https加密需求。 这种类型的证书特别适合于那些没有域名只有公网IP或者不方便使用域名的企业或个人。证书允许通过特定的IP地…