JAVA InputStream. 对网络数据帧自动分片

问题引入

组里的工程项目需要涉及安卓平台的网络收发。
我之前为这个项目写了一个WIFI通信模块,根据通讯规约必须传输byte流。因此模块使用的是最底层的InputStream。这也为之后埋下了后患。

通讯模块的写法如下:

OutputStream out = null;      //写
InputStream in = null;        //读

socket.connect(new InetSocketAddress(IP, Integer.parseInt(Port)), 3000);
out = socket.getOutputStream();
in = socket.getInputStream();

ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
while((len=in.read(buffer)) == -1 &&loopcount<Ma
在下述代码中如何 处理分片传输问题?package com.zzl.communication.modbus.service; import com.zzl.communication.base.AbstractCommunicationService; import com.zzl.communication.modbus.parser.ModbusMessageParser; import com.zzl.common.utils.HexUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ExecutorService; /** * Modbus 通信服务器 * v4 版本使用配置化的线程池,支持多客户端并发连接 * 实现了CommunicationService接口,提供标准的服务生命周期管理 */ @Slf4j @Service public class ModbusServerService { /** * Modbus服务器监听IP地址,从配置文件中读取 * 如果配置文件中未指定,默认使用192.168.0.121 */ @Value("${modbus.server.ip:192.168.0.121}") private String serverIp; /** * Modbus服务器监听端口,从配置文件中读取 * 如果配置文件中未指定,默认使用10000 */ @Value("${modbus.server.port:10000}") private int port; /** * 通信线程池,用于处理客户端连接和数据 * 通过构造函数注入,由ThreadPoolConfig配置类提供 */ private final ExecutorService threadPool; /** * 服务器Socket实例,用于监听客户端连接 */ private ServerSocket serverSocket; /** * 服务运行状态标志 * volatile保证多线程间的可见性 */ private volatile boolean running = true; @Autowired ModbusDataService modbusDataService; /** * 构造函数,注入配置的线程池 * @param threadPool 配置的线程池,使用@Qualifier指定具体的线程池Bean */ public ModbusServerService(@Qualifier("communicationThreadPool") ExecutorService threadPool) { this.threadPool = threadPool; } @PostConstruct public void start() { threadPool.submit(() -> { log.info("Modbus服务器线程启动:{}", Thread.currentThread().getName()); try { // 创建服务器Socket,设置最大连接队列为50 serverSocket = new ServerSocket(port, 50, InetAddress.getByName(serverIp)); log.info("Modbus Server started, listening on IP: {} Port: {}", serverIp, port); while (running) { try { // 接受新的客户端连接 Socket clientSocket = serverSocket.accept(); // 将客户端处理提交到线程池 threadPool.submit(() -> handleClient(clientSocket)); } catch (IOException e) { if (running) { log.error("Error accepting client connection: {}", e.getMessage()); } } } } catch (IOException e) { if (running) { log.error("Server socket error: {}", e.getMessage()); } } log.info("Modbus服务器线程结束:{}", Thread.currentThread().getName()); }); } @PreDestroy public void stop() { log.info("Shutting down Modbus server..."); running = false; if (serverSocket != null && !serverSocket.isClosed()) { try { serverSocket.close(); } catch (IOException e) { log.error("Error closing server socket: {}", e.getMessage()); } } } /** * 处理单个客户端连接 * @param clientSocket 客户端Socket连接 */ private void handleClient(Socket clientSocket) { String threadName = Thread.currentThread().getName(); String clientAddress = clientSocket.getRemoteSocketAddress().toString(); log.info("新的客户端连接:{} - 客户端地址: {}", threadName, clientAddress); try { // 设置读取超时,确保不会无限等待 clientSocket.setSoTimeout(2000); // 2秒超时 InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream(); // 使用循环持续读取客户端数据 while (running && !clientSocket.isClosed()) { try { // 先读取可用数据量 int available = inputStream.available(); if (available == 0) { // 如果没有可用数据,等待一小段时间再检查 // 初次设置150ms Thread.sleep(150); continue; } // 准备足够大的缓冲区 byte[] buffer = new byte[Math.max(512, available)]; int bytesRead = inputStream.read(buffer); if (bytesRead <= 0) { log.warn("读取到0字节,客户端可能已断开 - 客户端地址: {}", clientAddress); break; } // 处理接收到的数据 byte[] request = Arrays.copyOf(buffer, bytesRead); log.info("接收到Modbus报文: {} - 客户端地址: {}", HexUtils.bytesToHex(request), clientAddress); // 解析Modbus报文 log.info("{} 解析报文-begin - 客户端地址: {}", threadName, clientAddress); Map<String, Object> res = ModbusMessageParser.parseModbusMessage(request); if (res != null && !res.isEmpty()) { modbusDataService.saveModbusData(res, request); // 发送响应数据 byte[] response = createResponse(request); outputStream.write(response); outputStream.flush(); log.info("发送响应: {} - 客户端地址: {}", HexUtils.bytesToHex(response), clientAddress); } else { log.warn("解析结果为空或无效 - 客户端地址: {}", clientAddress); } log.info("{} 解析报文-end - 客户端地址: {}", threadName, clientAddress); // 短暂暂停,避免CPU占用过高 Thread.sleep(10); } catch (SocketException e) { log.error("Socket异常,可能连接已断开: {} - 客户端地址: {}", e.getMessage(), clientAddress); break; } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.warn("线程被中断 - 客户端地址: {}", clientAddress); break; } catch (IOException e) { log.error("IO异常: {} - 客户端地址: {}", e.getMessage(), clientAddress); break; } catch (Exception e) { log.error("处理报文时发生异常: {} - 客户端地址: {}", e.getMessage(), clientAddress); // 继续处理下一个报文,不中断连接 } } } catch (Exception e) { log.error("处理客户端连接时发生异常: {} - 客户端地址: {}", e.getMessage(), clientAddress); } finally { try { clientSocket.close(); log.info("客户端连接已关闭 - 客户端地址: {}", clientAddress); } catch (IOException e) { log.error("关闭客户端Socket时发生错误: {} - 客户端地址: {}", e.getMessage(), clientAddress); } } } /** * 创建响应报文 * @param request 请求报文 * @return 响应报文字节数组 */ private byte[] createResponse(byte[] request) { // TODO: 根据实际需求实现响应报文的生成逻辑 return new byte[]{(byte) 0xFF, 0x18, 0x00, 0x00}; } }
最新发布
06-14
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值