搜索词>>网络通讯 耗时0.0020
  • modbus tcp通讯modbus4j使用说明-java编程

    modbus tcp 通讯协议在Java编程中的使用。本文主要讲解Java编程中通过modbus4j工具类来实现modbus tcp通讯协议的通讯。包括通过modbus协议读取数据,写入数据的实现。<h2>一.什么是modbus</h2>   Modbus是由Modicon(现为施耐德电气公司的一个品牌)在1979年发明的,是全球第一个真正用于工业现场的总线协议。<br /> ModBus网络是一个工业通信系统,由带智能终端的可编程序控制器和计算机通过公用线路或局部专用线路连接而成。其系统结构既包括硬件、亦包括软件。它可应用于各种数据采集和过程监控。<br />   ModBus网络只有一个主机,所有通信都由他发出。网络可支持247个之多的远程从属控制器,但实际所支持的从机数要由所用通信设备决定。采用这个系统,各PC可以和中心主机交换信息而不影响各PC执行本身的控制任务。 <h2>二.Java实现modbus协议通讯</h2> Java编程中,使用modbus4j实现Java中的modbus协议通讯<br /> <br /> modbus4j实现了Java与modbus协议的以下几种通讯方式:<br /> modbus TCP/IP通讯<br /> modubs UDP/IP通讯<br /> modbus RTU/IP通讯<br /> <br /> 核心依赖:<br /> <strong>modbus4j.jar<br /> commons-lang3-3.0.jar<br /> //下载地址代码里有</strong><br /> Java读取工具类 <pre> <code class="language-java">package com.leftso.project.demo.modbus4j; import com.serotonin.modbus4j.BatchRead; import com.serotonin.modbus4j.BatchResults; import com.serotonin.modbus4j.ModbusFactory; import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.code.DataType; import com.serotonin.modbus4j.exception.ErrorResponseException; import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.exception.ModbusTransportException; import com.serotonin.modbus4j.ip.IpParameters; import com.serotonin.modbus4j.locator.BaseLocator; /** * modbus通讯工具类,采用modbus4j实现 * * @author lxq * @dependencies modbus4j-3.0.3.jar * @website https://github.com/infiniteautomation/modbus4j */ public class Modbus4jUtils { /** * 工厂。 */ static ModbusFactory modbusFactory; static { if (modbusFactory == null) { modbusFactory = new ModbusFactory(); } } /** * 获取master * * @return * @throws ModbusInitException */ public static ModbusMaster getMaster() throws ModbusInitException { IpParameters params = new IpParameters(); params.setHost("localhost"); params.setPort(502); // // modbusFactory.createRtuMaster(wapper); //RTU 协议 // modbusFactory.createUdpMaster(params);//UDP 协议 // modbusFactory.createAsciiMaster(wrapper);//ASCII 协议 ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议 master.init(); return master; } /** * 读取[01 Coil Status 0x]类型 开关数据 * * @param slaveId * slaveId * @param offset * 位置 * @return 读取值 * @throws ModbusTransportException * 异常 * @throws ErrorResponseException * 异常 * @throws ModbusInitException * 异常 */ public static Boolean readCoilStatus(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException { // 01 Coil Status BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset); Boolean value = getMaster().getValue(loc); return value; } /** * 读取[02 Input Status 1x]类型 开关数据 * * @param slaveId * @param offset * @return * @throws ModbusTransportException * @throws ErrorResponseException * @throws ModbusInitException */ public static Boolean readInputStatus(int slaveId, int offset) throws ModbusTransportException, ErrorResponseException, ModbusInitException { // 02 Input Status BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset); Boolean value = getMaster().getValue(loc); return value; } /** * 读取[03 Holding Register类型 2x]模拟量数据 * * @param slaveId * slave Id * @param offset * 位置 * @param dataType * 数据类型,来自com.serotonin.modbus4j.code.DataType * @return * @throws ModbusTransportException * 异常 * @throws ErrorResponseException * 异常 * @throws ModbusInitException * 异常 */ public static Number readHoldingRegister(int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException { // 03 Holding Register类型数据读取 BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType); Number value = getMaster().getValue(loc); return value; } /** * 读取[04 Input Registers 3x]类型 模拟量数据 * * @param slaveId * slaveId * @param offset * 位置 * @param dataType * 数据类型,来自com.serotonin.modbus4j.code.DataType * @return 返回结果 * @throws ModbusTransportException * 异常 * @throws ErrorResponseException * 异常 * @throws ModbusInitException * 异常 */ public static Number readInputRegisters(int slaveId, int offset, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException { // 04 Input Registers类型数据读取 BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType); Number value = getMaster().getValue(loc); return value; } /** * 批量读取使用方法 * * @throws ModbusTransportException * @throws ErrorResponseException * @throws ModbusInitException */ public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException { BatchRead<Integer> batch = new BatchRead<Integer>(); batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT)); batch.addLocator(1, BaseLocator.inputStatus(1, 0)); ModbusMaster master = getMaster(); batch.setContiguousRequests(false); BatchResults<Integer> results = master.send(batch); System.out.println(results.getValue(0)); System.out.println(results.getValue(1)); } /** * 测试 * * @param args */ public static void main(String[] args) { try { // 01测试 Boolean v011 = readCoilStatus(1, 0); Boolean v012 = readCoilStatus(1, 1); Boolean v013 = readCoilStatus(1, 6); System.out.println("v011:" + v011); System.out.println("v012:" + v012); System.out.println("v013:" + v013); // 02测试 Boolean v021 = readInputStatus(1, 0); Boolean v022 = readInputStatus(1, 1); Boolean v023 = readInputStatus(1, 2); System.out.println("v021:" + v021); System.out.println("v022:" + v022); System.out.println("v023:" + v023); // 03测试 Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,float Number v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上 System.out.println("v031:" + v031); System.out.println("v032:" + v032); // 04测试 Number v041 = readInputRegisters(1, 1, DataType.FOUR_BYTE_FLOAT);// Number v042 = readInputRegisters(1, 3, DataType.FOUR_BYTE_FLOAT);// System.out.println("v041:" + v041); System.out.println("v042:" + v042); // 批量读取 batchRead(); } catch (Exception e) { e.printStackTrace(); } } }</code></pre> <h2>三、测试</h2> 使用ModbusSlave模拟modbus协议<br /> slave中模拟数据如下<br /> <img alt="modbus slave模拟数据" class="img-thumbnail" src="/assist/images/blog/afe4a730-8fb5-4aa2-9f6b-ff09f4368459.png" style="height:856px; width:825px" /><br /> 运行工具类的main方法: <pre> <code>11:14:54.547 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 01 00 00 00 01 11:14:54.550 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.598 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 01 01 01 11:14:54.600 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 01 00 01 00 01 11:14:54.600 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.650 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 01 01 00 11:14:54.652 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 01 00 06 00 01 11:14:54.652 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.703 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 01 01 01 v011:true v012:false v013:true 11:14:54.704 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 00 00 01 11:14:54.704 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.755 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 01 11:14:54.757 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 01 00 01 11:14:54.757 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.807 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 00 11:14:54.810 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 02 00 01 11:14:54.810 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.860 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 01 v021:true v022:false v023:true 11:14:54.866 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 03 00 01 00 02 11:14:54.866 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.915 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 03 04 40 20 00 00 11:14:54.917 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 03 00 03 00 02 11:14:54.917 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:54.967 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 03 04 41 28 00 00 v031:2.5 v032:10.5 11:14:54.971 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 04 00 01 00 02 11:14:54.971 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:55.020 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 04 04 3F C0 00 00 11:14:55.021 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 04 00 03 00 02 11:14:55.021 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:55.072 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 07 01 04 04 40 40 00 00 v041:1.5 v042:3.0 11:14:55.074 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 00 00 00 00 06 01 02 00 00 00 01 11:14:55.074 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:55.123 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 00 00 00 00 04 01 02 01 01 11:14:55.125 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 01 00 00 00 06 01 03 00 01 00 02 11:14:55.125 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 11:14:55.179 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 01 00 00 00 07 01 03 04 40 20 00 00 2.5 true </code></pre> <br /> 观察输出结果与 slave上的模拟数据一致 <h2>四、Java通过modbus4j对数据的写入</h2> <strong>Modbus4jWriteUtils.java</strong> <pre> <code class="language-java">package com.leftso.project.demo.modbus4j; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.serotonin.modbus4j.ModbusFactory; import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.code.DataType; import com.serotonin.modbus4j.exception.ErrorResponseException; import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.exception.ModbusTransportException; import com.serotonin.modbus4j.ip.IpParameters; import com.serotonin.modbus4j.locator.BaseLocator; import com.serotonin.modbus4j.msg.ModbusResponse; import com.serotonin.modbus4j.msg.WriteCoilRequest; import com.serotonin.modbus4j.msg.WriteCoilResponse; import com.serotonin.modbus4j.msg.WriteCoilsRequest; import com.serotonin.modbus4j.msg.WriteCoilsResponse; import com.serotonin.modbus4j.msg.WriteRegisterRequest; import com.serotonin.modbus4j.msg.WriteRegisterResponse; import com.serotonin.modbus4j.msg.WriteRegistersRequest; /** * modbus4j写入数据 * * @author xq * */ public class Modbus4jWriteUtils { static Log log = LogFactory.getLog(Modbus4jWriteUtils.class); /** * 工厂。 */ static ModbusFactory modbusFactory; static { if (modbusFactory == null) { modbusFactory = new ModbusFactory(); } } /** * 获取tcpMaster * * @return * @throws ModbusInitException */ public static ModbusMaster getMaster() throws ModbusInitException { IpParameters params = new IpParameters(); params.setHost("localhost"); params.setPort(502); ModbusMaster tcpMaster = modbusFactory.createTcpMaster(params, false); tcpMaster.init(); return tcpMaster; } /** * 写 [01 Coil Status(0x)]写一个 function ID = 5 * * @param slaveId * slave的ID * @param writeOffset * 位置 * @param writeValue * 值 * @return 是否写入成功 * @throws ModbusTransportException * @throws ModbusInitException */ public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue) throws ModbusTransportException, ModbusInitException { // 获取master ModbusMaster tcpMaster = getMaster(); // 创建请求 WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue); // 发送请求并获取响应对象 WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request); if (response.isException()) { return false; } else { return true; } } /** * 写[01 Coil Status(0x)] 写多个 function ID = 15 * * @param slaveId * slaveId * @param startOffset * 开始位置 * @param bdata * 写入的数据 * @return 是否写入成功 * @throws ModbusTransportException * @throws ModbusInitException */ public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata) throws ModbusTransportException, ModbusInitException { // 获取master ModbusMaster tcpMaster = getMaster(); // 创建请求 WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata); // 发送请求并获取响应对象 WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request); if (response.isException()) { return false; } else { return true; } } /*** * 写[03 Holding Register(4x)] 写一个 function ID = 6 * * @param slaveId * @param writeOffset * @param writeValue * @return * @throws ModbusTransportException * @throws ModbusInitException */ public static boolean writeRegister(int slaveId, int writeOffset, short writeValue) throws ModbusTransportException, ModbusInitException { // 获取master ModbusMaster tcpMaster = getMaster(); // 创建请求对象 WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue); WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request); if (response.isException()) { log.error(response.getExceptionMessage()); return false; } else { return true; } } /** * * 写入[03 Holding Register(4x)]写多个 function ID=16 * * @param slaveId * modbus的slaveID * @param startOffset * 起始位置偏移量值 * @param sdata * 写入的数据 * @return 返回是否写入成功 * @throws ModbusTransportException * @throws ModbusInitException */ public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata) throws ModbusTransportException, ModbusInitException { // 获取master ModbusMaster tcpMaster = getMaster(); // 创建请求对象 WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata); // 发送请求并获取响应对象 ModbusResponse response = tcpMaster.send(request); if (response.isException()) { log.error(response.getExceptionMessage()); return false; } else { return true; } } /** * 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long) * * @param slaveId * @param offset * @param value * 写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long * @param registerCount * ,com.serotonin.modbus4j.code.DataType * @throws ModbusTransportException * @throws ErrorResponseException * @throws ModbusInitException */ public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType) throws ModbusTransportException, ErrorResponseException, ModbusInitException { // 获取master ModbusMaster tcpMaster = getMaster(); // 类型 BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType); tcpMaster.setValue(locator, value); } public static void main(String[] args) { try { //@formatter:off // 测试01 // boolean t01 = writeCoil(1, 0, true); // System.out.println("T01:" + t01); // 测试02 // boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true }); // System.out.println("T02:" + t02); // 测试03 // short v = -3; // boolean t03 = writeRegister(1, 0, v); // System.out.println("T03:" + t03); // 测试04 // boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 }); // System.out.println("t04:" + t04); //写模拟量 writeHoldingRegister(1,0, 10.1f, DataType.FOUR_BYTE_FLOAT); //@formatter:on } catch (Exception e) { e.printStackTrace(); } } } </code></pre> <br /> <img alt="浮点类型数据写入" class="img-thumbnail" src="/assist/images/blog/0cee0505f42e44bda68381524f669ba1.png" /><br /> <br /> modbus协议中常见功能代码说明:<br /> <img alt="function ID/code说明" class="img-thumbnail" src="/assist/images/blog/6333daae54c94281b86fba2676238c48.png" /><br />  
  • modbus tcp 通讯modbus-master-tcp Java使用说明

    引言    modbus tcp通讯Java的方案之前已经讲解过一种,modbus4j实现Java语言的modbus tcp协议通讯引言    modbus tcp通讯Java的方案之前已经讲解过一种,modbus4j实现Java语言的modbus tcp协议通讯。从上一个方案中我们不难发现modbus4j的通讯实现方式是同步的。实际应用中可能会读取大量的数据。同步处理对于应用的响应还是不太友好的。本博客主要讲解另外一种Java语言的modbux tcp通讯方案。那就是modbus-master-tcp。一.创建一个demo项目 创建一个简单的maven项目,项目结构图如下:​二.pom.xml maven依赖<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.leftso.demo.modbus</groupId> <artifactId>demo-modbus-master-slave</artifactId> <version>1.0</version> <dependencies> <dependency> <groupId>com.digitalpetri.modbus</groupId> <artifactId>modbus-master-tcp</artifactId> <version>1.1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>pom.xml注意,需要将java的编译版本指定到1.8.因为只有1.8以后才支持lambda表达式。配置完成后,我们观察引入的依赖包:​观察可以发现,modbus-master-tcp项目的底层是基于netty框架开发。天然的支持异步处理。在性能方面有很好的提升。三.编写modbus tcp读取案例package com.leftso.demo.modbus; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import com.digitalpetri.modbus.codec.Modbus; import com.digitalpetri.modbus.master.ModbusTcpMaster; import com.digitalpetri.modbus.master.ModbusTcpMasterConfig; import com.digitalpetri.modbus.requests.ReadCoilsRequest; import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest; import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest; import com.digitalpetri.modbus.requests.ReadInputRegistersRequest; import com.digitalpetri.modbus.responses.ReadCoilsResponse; import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse; import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse; import com.digitalpetri.modbus.responses.ReadInputRegistersResponse; import io.netty.buffer.ByteBuf; import io.netty.util.ReferenceCountUtil; /*** * modbus TCP协议Java通讯读取例子 * * @author xqlee * */ public class SimpleMasterExample { static ModbusTcpMaster master; /** * 获取TCP协议的Master * * @return */ public static void initModbusTcpMaster() { if (master == null) { // 创建配置 ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost").setPort(502).build(); master = new ModbusTcpMaster(config); } } /*** * 释放资源 */ public static void release() { if (master != null) { master.disconnect(); } Modbus.releaseSharedResources(); } /** * 读取HoldingRegister数据 * * @param address * 寄存器地址 * @param quantity * 寄存器数量 * @param unitId * id * @return 读取结果 * @throws InterruptedException * 异常 * @throws ExecutionException * 异常 */ public static Number readHoldingRegisters(int address, int quantity, int unitId) throws InterruptedException, ExecutionException { Number result = null; CompletableFuture<ReadHoldingRegistersResponse> future = master .sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId); ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理 if (readHoldingRegistersResponse != null) { ByteBuf buf = readHoldingRegistersResponse.getRegisters(); result = buf.readFloat(); ReferenceCountUtil.release(readHoldingRegistersResponse); } return result; } /** * 读取InputRegisters模拟量数据 * * @param address * 寄存器开始地址 * @param quantity * 数量 * @param unitId * ID * @return 读取值 * @throws InterruptedException * 异常 * @throws ExecutionException * 异常 */ public static Number readInputRegisters(int address, int quantity, int unitId) throws InterruptedException, ExecutionException { Number result = null; CompletableFuture<ReadInputRegistersResponse> future = master .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId); ReadInputRegistersResponse readInputRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理 if (readInputRegistersResponse != null) { ByteBuf buf = readInputRegistersResponse.getRegisters(); result = buf.readFloat(); ReferenceCountUtil.release(readInputRegistersResponse); } return result; } /** * 读取Coils开关量 * * @param address * 寄存器开始地址 * @param quantity * 数量 * @param unitId * ID * @return 读取值 * @throws InterruptedException * 异常 * @throws ExecutionException * 异常 */ public static Boolean readCoils(int address, int quantity, int unitId) throws InterruptedException, ExecutionException { Boolean result = null; CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity), unitId); ReadCoilsResponse readCoilsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理 if (readCoilsResponse != null) { ByteBuf buf = readCoilsResponse.getCoilStatus(); result = buf.readBoolean(); ReferenceCountUtil.release(readCoilsResponse); } return result; } /** * 读取readDiscreteInputs开关量 * * @param address * 寄存器开始地址 * @param quantity * 数量 * @param unitId * ID * @return 读取值 * @throws InterruptedException * 异常 * @throws ExecutionException * 异常 */ public static Boolean readDiscreteInputs(int address, int quantity, int unitId) throws InterruptedException, ExecutionException { Boolean result = null; CompletableFuture<ReadDiscreteInputsResponse> future = master .sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId); ReadDiscreteInputsResponse discreteInputsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理 if (discreteInputsResponse != null) { ByteBuf buf = discreteInputsResponse.getInputStatus(); result = buf.readBoolean(); ReferenceCountUtil.release(discreteInputsResponse); } return result; } public static void main(String[] args) { try { // 初始化资源 initModbusTcpMaster(); // 执行操作 // 读取模拟量 System.out.println(readHoldingRegisters(0, 4, 1)); System.out.println(readInputRegisters(0, 4, 1)); // 读取开关量 System.out.println(readCoils(0, 1, 1)); System.out.println(readDiscreteInputs(0, 1, 1)); System.out.println(readDiscreteInputs(2, 1, 1)); // 释放资源 release(); } catch (Exception e) { e.printStackTrace(); } } } 上面的代码中模拟量的读取需要注意,根据实际类型来读取相应的类型,例子中读取的double类型数据四.运行上面的案例演示modbus tcp数据读取 首先打开软件Modbus Slave(没有的可以百度下载)。启动连接:​连接完成后,创建四个文档如下图所示:​好了,现在运行我们刚才编写的Java demo程序,SimpleMasterExample:​通过执行结果可以看到与Modbus Slave软件中的文档数据一致。modbus tcp项目源码下载:demo-modbus-master-slave.zip
  • Spring boot hessian 通讯加密验证

    Spring boot hessian 通讯加密,Spring boot hessian RPC框架通讯之间的加密验证。实现安全的RPC访问<h2>引言</h2>     前面写了一篇<a rel="" target="_blank"href="http://www.leftso.com/blog/294.html" rel="" target="_blank" title="spring boot RPC 框架 Hessian">Spring boot RPC 框架 Hessian</a>。讲解了spring boot整合hessian的基本使用。这里主要围绕上一篇讲解的内容进行扩展讲解,hessian通讯的验证/加密实现安全的RPC访问。 <h2>一.自定义一个HessianServiceExporter</h2>     从上一篇博客<a rel="" target="_blank"href="http://www.leftso.com/blog/294.html" rel="" target="_blank" title="spring boot RPC 框架 Hessian">Spring boot RPC 框架 Hessian</a>中我们可以发现,hessian的server端是通过HessianServiceExporter发布hessian接口服务的。<br />     <br /> 目前hessian的server端项目结构图:<br /> <img alt="项目结构图" class="img-thumbnail" src="/assist/images/blog/4d20852096264d59b39b1dada4f5ba62.png" /><br /> 与上一篇对比,项目结构中多了一个类HessianServerProxyExporter。这个类主要是继承了HessianServiceExporter类并重写了handleRequest方法: <pre> <code class="language-java">package net.xqlee.project; import java.io.IOException; import java.util.Base64; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.remoting.caucho.HessianServiceExporter; import org.springframework.util.StringUtils; import org.springframework.web.util.NestedServletException; /** * 自定义hessian服务发布,可用于自定义验证服务 * * @author xqlee * */ public class HessianServerProxyExporter extends HessianServiceExporter { private static final Logger log = LoggerFactory.getLogger(HessianServerProxyExporter.class); @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String authorization = request.getHeader("authorization"); if (StringUtils.isEmpty(authorization)) { throw new NestedServletException("Auth Is Empty!"); } log.info(authorization); String[] authArr = authorization.trim().split(" "); String auth = authArr[1]; auth = new String(Base64.getDecoder().decode(auth)); String[] namePwdArr = auth.split(":"); String pwd = namePwdArr[1]; // 验证密码 if (!"123456".equals(pwd)) { throw new NestedServletException("Auth Fail!"); } super.handleRequest(request, response); } } </code></pre> 验证的核心代码就在重写的方法里面,这里只是简单的进行了密码的认证。<br /> <br /> 为了使验证生效,我们需要重写类DemoSpringbootRpcHessianApplication: <pre> <code class="language-java">package net.xqlee.project; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.remoting.caucho.HessianServiceExporter; import net.xqlee.project.service.HelloWorldService; @SpringBootApplication public class DemoSpringbootRpcHessianApplication { @Autowired private HelloWorldService helloWorldService; public static void main(String[] args) { SpringApplication.run(DemoSpringbootRpcHessianApplication.class, args); } // 发布服务 @Bean(name = "/HelloWorldService") public HessianServiceExporter accountService() { // HessianServiceExporter exporter = new HessianServiceExporter(); HessianServerProxyExporter exporter=new HessianServerProxyExporter(); exporter.setService(helloWorldService); exporter.setServiceInterface(HelloWorldService.class); return exporter; } } </code></pre> 可以看到只是进行了简单修改,使用我们自己定义的HessianServerProxyExporter 来发布hessian服务,而不是默认的HessianServiceExporter。 <h2>二.hessian客户端设置用户密码</h2> 客户端的项目结构图没变,如下:<br /> <img alt="项目结构图" class="img-thumbnail" src="/assist/images/blog/f10a8296447a4a95996ff163b14e22e9.png" /><br /> 客户端比之前只是在访问hessian接口的时候多设置了用户名和密码: <pre> <code class="language-java">package net.xqlee.project; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.remoting.caucho.HessianProxyFactoryBean; import net.xqlee.project.service.HelloWorldService; @SpringBootApplication public class DemoSpringbootRpcHessianClientApplication { public static void main(String[] args) { SpringApplication.run(DemoSpringbootRpcHessianClientApplication.class, args); } @Bean public HessianProxyFactoryBean helloClient() { HessianProxyFactoryBean factory = new HessianProxyFactoryBean(); factory.setServiceUrl("http://localhost:8083/HelloWorldService"); factory.setServiceInterface(HelloWorldService.class); factory.setPassword("123456"); factory.setUsername("download"); return factory; } } </code></pre> <span style="color:#ff0000"><span style="background-color:#ffff00">注意:HessianProxyFactoryBean中必须同时设置用户名和密码才能生效。</span></span><br /> <br /> 通过访问客户端的服务,通过断点到hessian的服务端HessianServerProxyExporter类,我们可以看到是如何传递用户名和密码参数的:<br /> <img alt="hessian传递认证信息" class="img-thumbnail" src="/assist/images/blog/3b8f60ff3153416fbe45c618a0e7f319.png" /><br /> 可以看到传递到服务端是通过HTTP协议的header传递的。并且传递的Basic认证头。这个头后面跟的字符串就是用户名:密码的Base64字符串,通过base64解密我们可以得到:<br /> <img alt="base64解密" class="img-thumbnail" src="/assist/images/blog/17312f236a3d454bac688634453d200e.png" /><br /> 用户名:密码已经显示出来。同样后面的判断也能实现我们的通讯验证。<br /> <br /> <br /> <strong>提示:<br /> 实际使用中,传递的密码或者用户请通过其他非对称加密方式加密后传输,服务端进行解密更为安全。</strong><br /> <br /> <br /> <br />  
  • Vue2.x父子组件相互通信

    一、前言Vue 2.x 使用期间,我们会创建众多组件,这里我们将讨论一下各个组件直接的相互通讯问题如何解决一、前言Vue 2.x 使用期间,我们会创建众多组件,这里我们将讨论一下各个组件直接的相互通讯问题如何解决。二、Vue 2.x组件相互通讯原理在 Vue.js 中,父子组件的关系可以总结为 props down, events up 。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息。看看它们是怎么工作的。​Vue 2.x组件相互通讯原理三、子组件 这里的子组件是模拟的一个搜索组件,包含返回、搜索等事件。只用关注组件的值传递即可。$title(child.vue) <template> <!--搜索条--> <div class="search-back-section searchHead flex-between animated" :class="activeClass"> <mu-button class="back" flat color="#444444" @click="back"><i class="fa fa-angle-left" aria-hidden="true"></i></mu-button> <mu-text-field :value="searchKeyword" v-model="text" :placeholder="'请输入:'+placeholder" :error-text="error" ></mu-text-field> <mu-button small class="btn" flat color="#444444" @click="search">搜索</mu-button> </div> </template> <script> import verify from "../assets/js/verify"; export default { name: "com-search-back-section", props: [ "back", //"返回按钮" "searchKeyword", //搜索关键字 "placeholder", //输入框提示 "error", //错误提示 "activeClass", //绑定CSS样式 ], data() { return { text: "", } }, watch:{ searchKeyword(val){ this.text=val; } }, created() { this.text = this.searchKeyword; }, methods: { search() { let msg = ""; if (verify.isEmpty(this.text)) { msg = this.text; } else { msg = false; } this.$emit("search", msg); } }, } </script> <style scoped> //样式代码与本文主题关系不大,省略 </style> 划重点props: [ "back", //"返回按钮" "searchKeyword", //搜索关键字----重点 "placeholder", //输入框提示 "error", //错误提示 "activeClass", //绑定CSS样式 ] watch:{ searchKeyword(val){ this.text=val; //---重点 } } 通过watch监视props中的属性值以及data中属性与数据的双向绑定,实现了外部数据的双向绑定。注意:父组件中定义子组件绑定属性的名称对应的是子组件props里面定义的名称一致search() { let msg = ""; if (verify.isEmpty(this.text)) { msg = this.text; } else { msg = false; } this.$emit("search", msg); //---重点 }通过$emit向外面抛出一个回调函数传递当前组件的值数据。四、父组件$title(patent.vue) <template> 其他省略--- <!--搜索框--> <search-back-section :active-class="isFixedState?'isFixed fadeInDown':''" :back="closeFullscreenDialog" :searchKeyword="searchKeyword" //-----重点 :placeholder="'关键字'" @search="searchBtn" :error="errorText" ></search-back-section> 其他省略--- </template> <script> let _self=null; export default { name: "door-home", data() { return { errorText: "", //错误信息 searchKeyword: "", //关键字 isFixedState:false //用于判断css样式选择,与主题无关 }, created() { _self=this; this.searchKeyword='默认的值'; //---重点 会看到初始化的子组件中输入框默认值为‘默认的值’(实现了父向子传递数据) }, methods: { /* * @"搜索"按钮 * */ searchBtn(msg) {------重点 通过子向外抛出的回调函数,子组件向父组件传递子组件中输入的内容值(实现了子向父传递数据) if (msg) { this.searchKeyword = msg; this.errorText = ""; } else { this.errorText = "请输入关键字."; return false; } } closeFullscreenDialog(){ //关闭搜索操作,与主题无关代码省略 } }, }; </script>注意:父组件中定义子组件绑定属性的名称对应的是子组件props里面定义的名称一致,传值是通过props传递所以必须一致。例子中的 :searchKeyword="searchKeyword"前面的searchKeyword是子组件的props定义的,后面这个是当前父组件定义的,后方这个searchKeyword可以随意其他名称
  • Zerotier网络管理_Zerotier免费稳定内网穿透工具

    据上一篇zerotier账号注册_zerotier免费稳定内网穿透工具 初步介绍了Zerotier和Zerotier的网络图,这篇主要针对Zerotier的网络管理基础操作进行讲解据上一篇zerotier账号注册_zerotier免费稳定内网穿透工具 初步介绍了Zerotier和Zerotier的网络图,这篇主要针对Zerotier的网络管理基础操作进行讲解。1​创建一个网络首先登录到Zerotier管理后台,登录方式参考zerotier账号注册_zerotier免费稳定内网穿透工具 文中有详细讲解。登录后切换到网络管理栏目,如下图:​ 接下来点击蓝色的Create Networ按钮,就可以创建一个子网络组了,创建后如下图:​上面的信息初步讲解下,ID 很重要,后续的客户端会通过这个ID进行join进这个网络组里面来。前面的文字描述也就是这一个意思。Zerotier管理子网络组 点击上面这个子网络,进入到管理界面:​网络管理主要有 Settings/Members/Flow Rules/Sharing几个模块。这里的模块是我收缩的,默认打开是展开的应该看起来很多东西的样子。下面进行Settings (设置)进行操作 基础设置界面:​在基础设置中,可以对网络组的名称/说明/接入方式进行修改。其中Access Control 的两个选择意思是,Private(客户端JOIN后需要审核),Public 不需要审核接下来是高级设置:​高级设置中,主要对路由进行设置,可以选择路由网段等操作。还有个就是能手动通过成员节点ID添加成员设备。成员设备管理​ 这里由于没有设备连接所以看起来是空的。如果有设备则是一个列表形式展示。下面是一个有成员的界面:​成员管理还有个新界面:​网络规则配置网络规则配置有点类似Linux系统的防火墙配置,普通用户这里默认即可不用改动。分享操作最后是一个分享的操作,授权分享设备出去。这个看需求一般也用不到。跳过了。删除网络组点击下面的红色Delete Network按钮进行删除:​​勾选上面的Yes...在点击确认删除。网络基础管理讲解完毕,下一步将会讲解成员添加和删除​
  • 群晖webdav实现外网映射网络驱动器

    一.群晖安装webdav套件​群晖安装WEBDAV套件二.群晖启用webdav套件​启用webdav套件这里的端口根据自己喜好设置,在局域网内访问的地址是:群晖IP:端口;外部网络访问则是:公网IP:路由映射出的公网端口三.window下安一.群晖安装webdav套件​群晖安装WEBDAV套件二.群晖启用webdav套件​启用webdav套件这里的端口根据自己喜好设置,在局域网内访问的地址是:群晖IP:端口;外部网络访问则是:公网IP:路由映射出的公网端口三.window下安装NetDrive2软件 NetDrive2软件下载点击上方的地址去下载NetDrive2,下载后解压并安装。步骤一:安装NetDrive2​步骤二:激活NetDrive2​修改hosts文件防止在线监测激活​步骤三:使用NetDrive2映射网络驱动器​通过以上操作已经实现了群晖webdav实现外网映射网络驱动器
  • Java网络编程如何从Filter中排除部分url

    Java网络编程如何从Filter中排除部分url,默认情况下,只要您为过滤器定义了网址格式,过滤器就不支持排除特定的网址格式,那么与该格式匹配的任何请求都会被过滤器处理,而不会有例外。Java如何从Filter中排除部分url,默认情况下,只要您为过滤器定义了网址格式,过滤器就不支持排除特定的网址格式,那么与该格式匹配的任何请求都会被过滤器处理,而不会有例外。 <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">从过滤器中排除网址的最简单方法是将您的过滤器映射到特定的模式。在早期开发阶段完成时,这是可行的,但是如果您修改生产环境中现有过滤器的URL模式,则必须重新映射所有现有的servlet URL才能达到目的,否则这可能是一个繁琐的过程。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">在本教程中,我们将演示如何以编程方式向现有过滤器添加排除功能。</span></span></span><br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start">1-自定义过滤器</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">自定义过滤器是您可以控制的过滤器。即您拥有修改其源代码的所有权利。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">假设我们有一个现有的Web应用程序,它通过LDAP来验证用户请求。所有servlet请求都通过映射到<strong><em>/ *的</em></strong><strong><em>LDAPAuthenticationFilter</em></strong>传递,如下所示:</span></span></span></p> <pre> <code class="language-xml"><filter> <filter-name>LDAPAuthenticationFilter</filter-name> <filter-class>com.programmer.gate.filters.LDAPAuthenticationFilter</filter-class> </filter> <filter-mapping> <filter-name>LDAPAuthenticationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">我们的过滤器只是验证请求,然后调用  <em>chain.doFilter()</em>:</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff"><u><em>LDAPAuthenticationFilter.java</em></u></span></span></span><br />  </p> <pre> <code class="language-java">import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class LDAPAuthenticationFilter implements Filter{ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // Authenticate the request through LDAP System.out.println("Authenticating the request through LDAP"); // Forward the request to the next filter or servlet in the chain. chain.doFilter(req, resp); } public void init(FilterConfig filterConfig) throws ServletException { } public void destroy() { // TODO Auto-generated method stub } }</code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">现在,假设我们要创建一个需要简单数据库认证的servlet,并且不需要通过LDAP。我们首先考虑创建一个新过滤器并将其映射到新servlet的特定URL模式。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">所以我们创建一个名为<strong><em>DatabaseAuthenticationFilter </em></strong>的新过滤器,它简单地通过数据库验证请求,然后调用  <em>chain.doFilter()</em>:</span></span></span></p> <pre> <code class="language-java">import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class DatabaseAuthenticationFilter implements Filter{ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // Authenticate the request through database then forward the request to the next filter or servlet in the chain System.out.println("Authenticating the request through database"); chain.doFilter(req, resp); } public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub } public void destroy() { // TODO Auto-generated method stub } }</code></pre> 我们在<em>web.xml中</em>定义我们的过滤器,以仅处理以<strong><em>/ DatabaseAuthenticatedServlet</em></strong>开头的特定URL : <pre> <code class="language-xml"><filter> <filter-name>DatabaseAuthenticationFilter</filter-name> <filter-class>com.programmer.gate.filters.DatabaseAuthenticationFilter</filter-class> </filter> <filter-mapping> <filter-name>DatabaseAuthenticationFilter</filter-name> <url-pattern>/DatabaseAuthenticatedServlet/*</url-pattern> </filter-mapping></code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这里的问题是像<em>/ DatabaseAuthenticatedServlet </em>这样的请求也会匹配根URL模式“/ *”,即我们的请求会通过2个认证过程:<em>LDAP</em>和<em>数据库, </em>排序取决于哪个过滤器首先在<em>web.xml</em>下定义  。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">为了解决这个问题,我们需要修改<strong><em>LDAPAuthenticationFilter,</em></strong>以便排除以<em>/ DatabaseAuthenticatedServlet</em>开头的URL <em>。 </em>通常情况下,人们会静态检查<em>doFilter()</em>方法中的请求的servlet URL,并在发现后直接绕过认证过程。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">这里我们更进一步,实现一个更动态的解决方案,它允许我们通过<em>web.xml</em>管理排除的URL 。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">以下是将排除功能添加到<strong><em>LDAPAuthenticationFilter</em></strong>的步骤:</span></span></span></p> <ul style="margin-left:10px; margin-right:0px"> <li>添加名为<em>excludedUrls</em>的<em>List <String></em>类型的新字段: <pre> <code class="language-java">private List excludedUrls; </code></pre> </li> <li>在<em>init()</em>方法内部,使用<strong><em>FilterConfig</em></strong>读取名为<em>excludedUrls</em>的配置属性<strong><em>,</em></strong>该属性应该用逗号分隔,以便我们排除尽可能多的URL。 <pre> <code class="language-java">public void init(FilterConfig filterConfig) throws ServletException { String excludePattern = filterConfig.getInitParameter("excludedUrls"); excludedUrls = Arrays.asList(excludePattern.split(",")); }</code></pre> </li> <li>修改<em>doFilter()</em>以检查请求的URL是否属于预定义排除的URL列表,如果是这样,那么只需将请求转发到链中的下一个过滤器或servlet,否则执行认证逻辑。 <pre> <code class="language-java">public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { String path = ((HttpServletRequest) req).getServletPath(); if(!excludedUrls.contains(path)) { // Authenticate the request through LDAP System.out.println("Authenticating the request through LDAP"); } // Forward the request to the next filter or servlet in the chain. chain.doFilter(req, resp); }</code></pre> </li> <li>现在在<em>web.xml中</em>,您可以控制要从LDAP身份验证中排除哪个URL,而无需更改任何代码: <pre> <code class="language-xml"><filter> <filter-name>LDAPAuthenticationFilter</filter-name> <filter-class>com.programmer.gate.filters.LDAPAuthenticationFilter</filter-class> <init-param> <param-name>excludedUrls</param-name> <!-- Comma separated list of excluded servlets --> <param-value>/DatabaseAuthenticatedServlet,/UnAuthenticatedServlet</param-value> </init-param> </filter></code></pre> </li> </ul> <br /> 这是在添加排除功能后<strong><em>LDAPAuthenticationFilter的代码样子</em></strong>: <pre> <code class="language-java">import java.io.IOException; import java.util.Arrays; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class LDAPAuthenticationFilter implements Filter{ private List excludedUrls; public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { String path = ((HttpServletRequest) req).getServletPath(); if(!excludedUrls.contains(path)) { // Authenticate the request through LDAP System.out.println("Authenticating the request through LDAP"); } // Forward the request to the next filter or servlet in the chain. chain.doFilter(req, resp); } public void init(FilterConfig filterConfig) throws ServletException { String excludePattern = filterConfig.getInitParameter("excludedUrls"); excludedUrls = Arrays.asList(excludePattern.split(",")); } public void destroy() { // TODO Auto-generated method stub } }</code></pre>   <h2 style="margin-left:0px; margin-right:0px; text-align:start">2-第三方过滤器</h2> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">第三方过滤器是您无法控制的过滤器。即你不能修改他们的源代码。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">在本节中,我们稍微修改我们的示例,并使用CAS认证而不是LDAP。这是我们如何在<em>web.xml中</em>定义我们的CAS认证过滤器:</span></span></span></p> <pre> <code class="language-xml"><filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://localhost:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>localhost</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></code></pre> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">CAS认证是通过第三方库完成的,现在为了支持数据库认证,我们不能修改CAS的源代码,就像我们在前面的LDAP例子中所做的那样。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">从第三方过滤器中排除URL的解决方案是使用新的自定义过滤器来包装它,该自定义过滤器仅添加排除功能并将过滤器逻辑委托给包装的类。</span></span></span></p> <p style="text-align:start"><span style="color:#333333"><span style="font-family:Tahoma,Arial,Verdana,sans-serif"><span style="background-color:#ffffff">以下是将排除功能添加到CAS认证的步骤:</span></span></span></p> <ul style="margin-left:10px; margin-right:0px"> <li>创建一个名为<strong><em>CASCustomAuthenticationFilter</em></strong>的新过滤器,如下所示: <pre> <code class="language-java">public class CASCustomAuthenticationFilter implements Filter{ private AuthenticationFilter casAuthenticationFilter = new AuthenticationFilter(); private List excludedUrls; public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { String path = ((HttpServletRequest) req).getServletPath(); if(!excludedUrls.contains(path)) { // Authenticate the request through CAS casAuthenticationFilter.doFilter(req,resp,chain); } // Forward the request to the next filter or servlet in the chain. chain.doFilter(req, resp); } public void init(FilterConfig arg0) throws ServletException { String excludePattern = filterConfig.getInitParameter("excludedUrls"); excludedUrls = Arrays.asList(excludePattern.split(",")); casAuthenticationFilter.init(); } public void destroy() { casAuthenticationFilter.destroy(); } }</code></pre> </li> </ul>        我们的自定义过滤器通过合成包装了CAS认证过滤器,其主要目的是通过CAS来管理要认证的URL,而我们并未触及CAS认证过程。 <ul> <li>在<em>web.xml中</em>,我们将过滤器定义更改为使用  <strong><em>CASCustomAuthenticationFilter</em></strong>而不是缺省的CAS实现: <pre> <code class="language-xml"><filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>com.programmer.gate.filters.CASCustomAuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https:localhost:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>localhost</param-value> </init-param> <init-param> <param-name>excludeUrls</param-name> <param-value>/DatabaseAuthenticatedServlet</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></code></pre> </li> </ul>
  • 群晖接入水星(MERCURY)MIPC251C-4网络摄像机

    一.水星(MERCURY)MIPC251C-4网络摄像机购入途径​我的这个摄像头是在假货长出APP(拼多多)上购入的,84.9.听说这平台的低价买过69的没遇到一.水星(MERCURY)MIPC251C-4网络摄像机购入途径​我的这个摄像头是在假货长出APP(拼多多)上购入的,84.9.听说这平台的低价买过69的没遇到。二.水星(MERCURY)MIPC251C-4网络摄像机联网 这款设备有两个联网方式:有线网口链接无限WiFi连接我这里使用的是无线连接。吐槽一下如今国内的大部分厂家,只要需要跟手机APP配合的都需要注册一个他们的账户(华硕路由除外)。无限连接第一步就是下载水星安防APP,下载地址在购买的说明书里面有个二维码,扫描即可。注册水星的账户,可以通过手机号码也可以通过邮箱注册。登录到APP点击添加摄像头按步骤执行完毕即可。三.水星(MERCURY)MIPC251C-4网络摄像机介入到群晖 首先群晖安装SurveillanceStation套件安装好以后打开套件如下:​接下来是添加水星(MERCURY)MIPC251C-4网络摄像机到群晖的SurveillanceStation里面。点击上图的网络摄像机图标:​就是点击上面这个。打开后如下图:​上图的摄像头是因为我之前已经添加上了。下面说说添加步骤第一步:点击新增,如下图: ​选择添加摄像机,然后:​这里我选择的快速设置。继续下一步​这里我们可以手动输入信息也可以点击那个放大镜进行局域网内搜索。下面是局域网搜索界面:​要选择通用接口(ONVIF)因为水星并不是群晖的合作产品。好了回来填写网络摄像头信息:​端口是2020.如果从刚才的搜索点击过来,可能会默认填80,记得修改然后默认用户:admin密码:123456点击测试连接,成功会提示。最后点击完成添加即可。目前已知不足:云台的控制功能无法使用。最后提示:家用怕隐私问题的可以在路由器把摄像机的IP限制公网访问。然后通过群晖来看就好啦。
  • Zerotier成员的添加删除管理_Zerotier免费稳定内网穿透工具

    接上一篇Zerotier网络管理_Zerotier免费稳定内网穿透工具 网络管理讲解,本文将会讲解网络管理里面的成员设备添加删除等管理接上一篇Zerotier网络管理_Zerotier免费稳定内网穿透工具 网络管理讲解,本文将会讲解网络管理里面的成员设备添加删除等管理。​添加成员设备到网络组有两个类型,一个是基础设置设置的公开(PUBLIC)另一个是私有(PRIVATE)先说PUBLIC(公开)模式添加成员设备首先成员设备安装相应的客户端软件,比如我这里用window 10作为测试,首先去zerotier官网下载window版本的客户端。下载地址:https://download.zerotier.com/dist/ZeroTier%20One.msi下载后双击安装即可​提示:安装过程如果有360请注意同意操作,或者在安装前关闭360等杀毒软件。安装好以后点击运行,​选择软件然后鼠标邮件弹出菜单,选择Join Network..​内部填入网组的ID,然后点击Join这个时候去zerotier管理后台,进入网组管理的成员管理去查看,就有设备啦:​这样我们就成功将一台window系统的设备接入到zerotier的一个子网组里面去了,当然一个巴掌拍不响,接下来我将添加一个群晖设备进去。这个时候网组就有两台设备了:​通过列表我们可以看到两个设备的公网IP不同(Physical IP公网IP),也就说两个设备并不在同一个网络环境下。接下来我们通过window设备,使用群晖的zerotier内网IP 10.241.142.151来访问,试试看能否成功呢?​我们可以看到访问成功啦!!!!!!!!!!再说PRIVATE(私有)模式添加成员设备私有添加设备需要先再管理后台手动邀请,也就是下面这个界面​这里填写的是一个设备号其他与公共模式相同操作。Enjoy!
  • httpclient4.5使用详解 httpclient 4.5 post传递json参数

    httpclient4.5使用详解 httpclient 4.5 post传递json参数httpclient4.5使用详解 httpclient 4.5 post传递json参数 <pre> <code class="language-java">import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; /** * 工具类 httpclient4.5 * * @author xq * */ public class HttpclientUtils { /** * post请求 json参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doPost(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpPost httpPost = new HttpPost(url); httpPost.addHeader("Content-Type", "application/json"); httpPost.setEntity(new StringEntity(bodyJsonParams)); if (headers != null && headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpPost.addHeader(key, value); } } return execute(httpPost); } /** * post k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doPost(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpPost httpPost = new HttpPost(url); if (params != null && params.keySet().isEmpty()) { List<NameValuePair> list = new ArrayList<>(); Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); list.add(new BasicNameValuePair(key, value)); } httpPost.setEntity(new UrlEncodedFormEntity(list)); } if (headers != null && headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpPost.addHeader(key, value); } } return execute(httpPost); } /** * get请求 * * @param url * @param params * @param headers * @return * @throws IOException * @throws ClientProtocolException */ public static String doGet(String url, Map<String, String> params, Map<String, String> headers) throws IOException { // 参数 StringBuilder paramsBuilder = new StringBuilder(url); if (params != null && params.keySet().isEmpty()) { if (url.indexOf("?") == -1) { paramsBuilder.append("?"); } List<NameValuePair> list = new ArrayList<>(); Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); list.add(new BasicNameValuePair(key, value)); } String paramsStr = EntityUtils.toString(new UrlEncodedFormEntity(list)); paramsBuilder.append(paramsStr); } HttpGet httpGet = new HttpGet(paramsBuilder.toString()); // 头 if (headers != null && headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpGet.addHeader(key, value); } } return execute(httpGet); } /** * 执行请求并返回string值 * * @param httpUriRequest * @return * @throws IOException */ private static String execute(HttpUriRequest httpUriRequest) throws IOException { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { CloseableHttpResponse response = httpClient.execute(httpUriRequest); if (response.getStatusLine().getStatusCode() == 200) {// 请求成功状态 try (BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(response.getEntity().getContent()))) { String result=""; String tmp=null; while((tmp=bufferedReader.readLine())!=null){ result+=tmp; } return result; } } } return null; } } </code></pre>