# JJF-Protocol **Repository Path**: skyyan/tcp-protocol ## Basic Information - **Project Name**: JJF-Protocol - **Description**: 基于Netty的JJF(豫)360-2023通讯协议实现 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2025-08-22 - **Last Updated**: 2025-09-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # JJF-Protocol 基于Netty的JJF(豫)360-2023通讯协议实现,提供生产级别的客户端和服务端代码。 ## 项目特性 - **生产级代码质量**:完整的错误处理、日志记录、资源管理 - **高性能网络通信**:基于Netty NIO框架,支持高并发连接 - **健壮的编解码**:使用ReplayingDecoder处理TCP粘包/半包问题 - **完整的协议支持**:实现C.1-C.9报文格式,支持CRC8校验和小端字节序 - **可扩展架构**:工厂模式设计,易于添加新的报文类型 - **自动重连机制**:客户端支持断线重连和心跳保活 - **详细的中文注释**:每个类和方法都有详细的功能说明 - **交互式客户端**:支持用户主动触发各种报文发送和处理响应 ## 项目结构 ``` src/main/java/com/skyyan/netty/jjf/ ├── Bootstrap.java # 入口类 用法: java -jar tcp-protocol.jar [server|client] 来判断启动服务端还是客户端 ├── protocol/ # 协议定义 │ ├── FrameConstants.java # 协议常量定义 │ ├── Message.java # 报文基类 │ ├── RegisterMessage.java # C.4 手持设备注册报文 │ ├── RegisterConfirmMessage.java # C.5 手持设备注册确认报文 │ ├── BalanceQueryMessage.java # C.6 余额查询报文 │ ├── RechargeMessage.java # C.7 充值报文 │ ├── OpenCardMessage.java # C.9 开卡报文 │ └── WaterElectricityPriceQueryMessage.java # C.9 阶梯水电价查询报文 ├── codec/ # 编解码器 │ ├── Crc8Util.java # CRC8校验工具 │ ├── MessageFactory.java # 消息工厂 │ ├── JjfMessageEncoder.java # 消息编码器 │ └── JjfMessageDecoder.java # 消息解码器 ├── server/ # 服务端 │ ├── ClientInfo.java # 客户端信息管理 │ ├── ServerHandler.java # 服务端业务处理器 │ └── JjfServer.java # 服务端启动类 ├── client/ # 客户端 │ ├── ClientHandler.java # 客户端业务处理器 │ ├── JjfClient.java # 客户端启动类 │ ├── MessageHandler.java # 消息处理接口 │ ├── InteractiveClient.java # 交互式客户端处理器 │ └── handler/ # 消息处理器实现 │ ├── RegisterMessageHandler.java │ ├── RegisterConfirmMessageHandler.java │ ├── BalanceQueryMessageHandler.java │ ├── RechargeMessageHandler.java │ ├── OpenCardMessageHandler.java │ ├── WaterElectricityPriceQueryMessageHandler.java │ └── DefaultMessageHandler.java └── util/ # 工具类 ├── BcdUtil.java # BCD码转换工具 ├── ChineseUtils.java # 中文处理工具 ├── CardNumberUtil.java # 卡号处理工具 ├── FixedLengthUtf8Encoder.java # 固定长度UTF-8编码器 ├── Str2HexUtils.java # 字符串与十六进制转换工具 └── JJFByteUtil.java # JJF协议字节处理工具 ``` ## 支持的报文类型 | AFN | 报文类型 | 方向 | 实现状态 | |------|--------------|------|----------| | 0x91 | C.4 手持设备注册 | 上行/下行 | ✅ 完整实现 | | 0x92 | C.5 手持设备注册确认 | 上行/下行 | ✅ 完整实现 | | 0x98 | C.6 余额查询 | 上行/下行 | ✅ 完整实现 | | 0x95 | C.7 充值 | 上行/下行 | ✅ 完整实现 | | 0x99 | C.8 阶梯水电价查询 | 上行/下行 | ✅ 完整实现 | | 0x97 | C.9 开卡 | 上行/下行 | ✅ 完整实现 | ## 类详细描述 ### 协议层 (protocol) #### Message.java 报文基类,定义了所有报文的通用接口和基本属性: - 控制域(Control Field) - 地址域(Address Field) - AFN功能码(Application Function Number) - 用户数据(User Data) - CRC校验和编解码方法 #### FrameConstants.java 协议常量定义类,包含: - 控制域常量(上行/下行) - AFN功能码常量 - 协议帧格式常量 #### RegisterMessage.java 手持设备注册报文(C.4)实现: - Uplink:设备向服务器发送注册请求 - Downlink:服务器向设备发送注册响应 - 包含设备注册号的生成和解析 #### RegisterConfirmMessage.java 手持设备注册确认报文(C.5)实现: - Uplink:设备向服务器发送注册确认 - Downlink:服务器向设备发送确认响应 #### BalanceQueryMessage.java 余额查询报文(C.6)实现: - Uplink:设备向服务器发送余额查询请求 - 包含设备注册号、充值机序列号、农户注册号等字段 - 支持余额、用户名、卡号等信息解析 #### RechargeMessage.java 充值报文(C.7)实现: - Uplink:设备向服务器发送充值请求 - Downlink:服务器向设备发送充值响应 - 包含充值金额、订单号等字段 #### OpenCardMessage.java 开卡报文(C.9)实现: - Uplink:设备向服务器发送开卡请求 - Downlink:服务器向设备发送开卡响应 - 支持订单号、姓名、电话、身份证号、区域码、卡号等字段 #### WaterElectricityPriceQueryMessage.java 阶梯水电价查询报文(C.9)实现: - Uplink:设备向服务器发送价格查询请求 - Downlink:服务器向设备发送价格信息 - 支持多级水价和电价查询 ### 编解码层 (codec) #### Crc8Util.java CRC8校验工具类: - 实现CRC8多项式计算 - 提供数据校验和生成方法 #### MessageFactory.java 消息工厂类: - 根据AFN和控制域创建对应的报文对象 - 支持所有已实现的报文类型 #### JjfMessageEncoder.java JJF协议编码器: - 将报文对象编码为字节数组 - 添加协议帧头、长度、CRC校验等 #### JjfMessageDecoder.java JJF协议解码器: - 将字节数组解码为报文对象 - 处理TCP粘包/半包问题 - 使用ReplayingDecoder确保数据完整性 ### 服务端 (server) #### ClientInfo.java 客户端信息管理类: - 存储已连接客户端的信息 - 管理客户端状态和会话 #### ServerHandler.java 服务端业务处理器: - 处理客户端连接、断开事件 - 分发不同类型的报文到对应处理方法 - 实现心跳检测和超时处理 #### JjfServer.java 服务端启动类: - 配置Netty服务端参数 - 启动监听指定端口 - 管理连接和资源释放 ### 客户端 (client) #### ClientHandler.java 客户端业务处理器: - 处理与服务器的通信逻辑 - 实现注册、心跳、业务请求等处理 - 分发不同类型的报文到对应处理方法 #### JjfClient.java 客户端启动类: - 配置Netty客户端参数 - 实现自动重连机制 - 提供交互式命令行界面 - 处理用户主动触发的各种报文 #### InteractiveClient.java 交互式客户端处理器: - 支持用户主动触发各种报文发送 - 处理服务器返回的响应报文 - 使用CompletableFuture实现异步等待响应 - 采用策略模式处理不同类型的服务器响应 #### MessageHandler.java 消息处理接口: - 定义统一的消息处理接口 - 支持消息类型判断和处理方法 #### handler/*MessageHandler.java 各种消息处理器实现: - RegisterMessageHandler: 处理注册消息 - RegisterConfirmMessageHandler: 处理注册确认消息 - BalanceQueryMessageHandler: 处理余额查询消息 - RechargeMessageHandler: 处理充值消息 - OpenCardMessageHandler: 处理开卡消息 - WaterElectricityPriceQueryMessageHandler: 处理阶梯水电价查询消息 - DefaultMessageHandler: 默认消息处理器 ### 工具类 (util) #### BcdUtil.java BCD码转换工具: - BCD码与整数之间的相互转换 - 支持不同长度的BCD码处理 #### ChineseUtils.java 中文处理工具: - 中文字符串与字节数组的相互转换 - 支持固定长度编码和解码 #### CardNumberUtil.java 卡号处理工具: - 卡号格式化和解析 - 支持不同格式的卡号处理 #### FixedLengthUtf8Encoder.java 固定长度UTF-8编码器: - 实现固定长度的UTF-8字符串编码 - 支持填充和截断处理 #### Str2HexUtils.java 字符串与十六进制转换工具: - 字符串与十六进制字符串的相互转换 - 支持字节数组处理 #### JJFByteUtil.java JJF协议字节处理工具: - 协议特定的字节处理方法 - 支持小端字节序处理 ## 下发报文模拟 ****************************************** ## 注册确认:68126800 91 11223344556677889900aabbccddeeff 2F16 ## 注册确确认 68036800 92 00 8A16 ## 充值余额查询 68186800 98 00 D3FFFFFF E5 BC A0 E4 B8 89 000000 000000 005DFD3B14 71 16 {control=0x00, afn=0x98, address=null, statusCode=0x00, balance=-45, username=张三, cardNo=1576876820, cardNoHex=00 5D FD 3B 14} - 状态码: 0x00 - 余额: -45分 - 用户名: 张三 - 卡号: 1576876820 ### 余额前端显示 要除以100保留2位小数 ## 充值报文:681b6800 95 00 11FFFFFF E5 BC A0 E4 B8 89 000000 000000 9885250827182420 DB16 {control=0x00, afn=0x95, address=null, statusCode=0x00, balance=-239, username=张三, orderNo=9885250827182420} - 状态码: 0x00 - 充值金额: -239分 - 交易流水号: 9885250827182420 ### 流水号BCD码(订单号生成规则) 地址域 后2个字节 +年月日时分秒 共占8个字节 生成BCD码 ;如果是9885为设备序列号后2位 250827182420 为年月日时分秒 ## 查询阶梯:68156800 99 00 003039 00393A 00393B 00393C 00393D 00303A F716 - 状态码: 0x00 - 1阶水价: 12345元 - 2阶水价: 14650元 - 3阶水价: 14651元 - 1阶电价: 14652元 - 2阶电价: 14653元 - 3阶电价: 12346元 ### 阶梯水电价 查出来后前端显示要除以1000保留三位小数 ## 开卡报文:681B6800 97 9885250827182420 010203040506070809000a0b0c0d0e0f 00 0A16 - 开卡下行报文内容: OpenCardMessage.Downlink{orderId='9885250827182420'orderId BCD HEX='[B@767bec36', farmerId=0f0e0d0c0b0a00090807060504030201, status=0x0, statusDesc='成功'} - 状态码: 0x00 - 订单号: 9885250827182420 - 卡号: 0f0e0d0c0b0a00090807060504030201 ### 流水号BCD码(订单号生成规则) 地址域 后2个字节 +年月日时分秒 共占8个字节 生成BCD码 例如 9885为设备序列号后2位 250827182420 为年月日时分秒 ******************************* ## 快速开始 ### 环境要求 - JDK 8 或更高版本 - Maven 3.6 或更高版本 ### 编译项目 ```bash mvn clean compile ``` ### 运行服务端 ```bash # 使用默认端口1883 mvn exec:java -Dexec.mainClass="com.skyyan.netty.jjf.server.JjfServer" # 或指定端口 mvn exec:java -Dexec.mainClass="com.skyyan.netty.jjf.server.JjfServer" -Dexec.args="1883" ``` ### 运行客户端 ```bash # 连接到默认服务器localhost:1883 mvn exec:java -Dexec.mainClass="com.skyyan.netty.jjf.client.JjfClient" # 或指定服务器地址和端口 mvn exec:java -Dexec.mainClass="com.skyyan.netty.jjf.client.JjfClient" -Dexec.args="127.0.0.1 1883" ``` ### 打包可执行JAR ```bash mvn clean package java -jar target/tcp-protocol-1.0-SNAPSHOT.jar ``` ## 协议说明 ### 帧格式 ``` +--------+--------+--------+--------+--------+--------+--------+--------+ | 起始符 | 长度L (1字节) | 起始符 | 控制域(1字节)+地址域(1字节)+AFN+用户数据 | CRC8 | 结束符 | | 68H | (小端) | 68H | (L字节) | | 16H | +--------+--------+--------+--------+--------+--------+--------+--------+ ``` 上行报文 ``` +--------+--------+--------+--------+--------+--------+--------+--------+ | 起始符 | 长度L (1字节) | 起始符 | 控制域(1字节)+地址域(1字节)+AFN+用户数据 | CRC8 | 结束符 | | 68H | (小端) | 68H | (L字节) | | 16H | +--------+--------+--------+--------+--------+--------+--------+--------+ ``` 下行报文 ``` +--------+--------+--------+--------+--------+--------+--------+--------+ | 起始符 | 长度L (1字节) | 起始符 | 控制域(1字节)+AFN(1字节)+用户数据 | CRC8 | 结束符 | | 68H | (小端) | 68H | (L字节) | | 16H | +--------+--------+--------+--------+--------+--------+--------+--------+ ``` ### 地址域结构 根据不同报文类型,地址域长度可能不同: - **标准地址域 (9字节)**: - A1 (3字节):行政区划码,BCD格式 - A2 (3字节):镇村编码,BCD格式 - A3 (3字节):测站编码,二进制格式,小端字节序 - **扩展地址域 (19字节或更多)**: - 设备注册号:16字节(低位在前,高位在后) - 充值管理机序列号:3字节(低位在前,高位在后) ### CRC8校验 - 多项式:x^8 + x^2 + x + 1 (0x07) - 校验范围:控制域 + 地址域 + AFN + 用户数据 ## 交互式客户端使用说明 客户端启动后提供交互式命令行界面,支持用户主动触发以下操作: 1. **发送注册报文** - 手持设备向服务器注册 2. **发送注册确认报文** - 确认注册成功 3. **发送余额查询报文** - 查询用户账户余额 4. **发送充值报文** - 为用户账户充值 5. **发送开卡报文** - 为用户开卡 6. **发送阶梯水电价查询报文** - 查询当前阶梯水电价格 每个操作都会等待服务器响应(最多10秒),并对响应报文进行详细解析和展示。 ## 扩展新报文 要添加新的报文类型,请按以下步骤操作: 1. **创建报文类**:在`protocol`包下创建新的报文类,继承`Message`基类 2. **更新常量**:在`FrameConstants`中添加新的AFN常量 3. **更新工厂**:在`MessageFactory.createMessage()`中添加新的case分支 4. **添加处理逻辑**:在`ServerHandler`和`ClientHandler`中添加对应的处理方法 5. **添加交互支持**:在`JjfClient`中添加用户交互支持 ### 示例:添加充值确认报文 ```java // 1. 创建报文类 public class RechargeAckMessage extends Message { public static final byte AFN_CODE = (byte) 0x9A; public RechargeAckMessage(byte[] address, String orderId, int status) { // 实现构造逻辑 } // 实现toBytes和解析相关方法 } // 2. 更新MessageFactory case FrameConstants.AFN_RECHARGE_ACK: return new RechargeAckMessage(control, address, userData); // 3. 更新处理器 private void handleRechargeAckMessage(ChannelHandlerContext ctx, RechargeAckMessage msg) { // 实现充值确认处理逻辑 } ``` ## 配置说明 ### 服务端配置 - **默认端口**:1883 - **Boss线程数**:1 - **Worker线程数**:CPU核心数 × 2 - **连接超时**:读60秒,写30秒,总90秒 ### 客户端配置 - **重连次数**:最多10次 - **重连间隔**:5秒 - **心跳间隔**:30秒 - **连接超时**:读90秒,写30秒,总120秒 ## 日志配置 项目使用SLF4J + Logback进行日志记录,配置文件位于`src/main/resources/logback.xml`。 - **控制台输出**:INFO级别及以上 - **文件输出**:所有级别,按日期和大小滚动 - **应用日志**:DEBUG级别,便于调试 ## 性能特性 - **零拷贝**:使用Netty的ByteBuf,减少内存拷贝 - **对象池化**:重用编解码器实例 - **异步处理**:所有I/O操作均为异步非阻塞 - **内存管理**:自动释放ByteBuf资源,防止内存泄漏 ## 故障排除 ### 常见问题 1. **连接被拒绝**:检查服务端是否启动,端口是否正确 2. **CRC校验失败**:检查数据传输是否完整,字节序是否正确 3. **解码异常**:检查报文格式是否符合协议规范 4. **心跳超时**:检查网络连接是否稳定 5. **编译错误**:检查Java版本是否符合要求(推荐使用Java 8) ### 调试技巧 1. 启用DEBUG日志查看详细的编解码过程 2. 使用Wireshark抓包分析网络数据 3. 检查CRC计算是否正确 4. 验证字节序处理是否符合协议要求 ## 许可证 本项目仅供学习和参考使用。