四月份的时候公司有个水库项目需要写,老板就把这个任务妥妥的交给我了,项目上有很多检测数据的传感器,说实话,第一次接触完全没有经验,后来经过了解才知道这玩意儿直接通过tcp通信就可以接收数据了,下面分享一下我写的通信模块源码:
//由于是多个终端设备,所以需要给每个链接都放入到一个线程中,方便后面调用
public static ExecutorService threadPool = new ThreadPoolExecutor(
15, // 核心线程数
20, // 最大线程数
0L, // 线程空闲超时时间(单位:秒)
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(10),// 任务队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
//tcp通信其实就只要new一个socket服务就可以了,终端通过ip和端口就可以主动链接
try {
ServerSocket serverSocket = new ServerSocket(port);
log.info("Socket服务已启动,占用端口: {}", port);
while (true) {
Socket socket = serverSocket.accept();
log.info("新设备上线{}",socket.toString());
//清理一下离线设备
SocketHandler.clear();
// ClientSocket相当于是一个链接的实体类,在里面设置了socket、输入输出流,以及一个客户端的id名,方便加入线程以及集合中统一管理
ClientSocket register = new ClientSocket(socket);
register.setClientId(socket.toString());
register.setSocket(socket);
//解释一下,这里的add是我一个存储链接的一个集合,每个终端上线后都单独存入到这个集合中。
add(register);
//这里就把这个客户端的socket加入到了线程池中了
threadPool.execute(new ClientSocket(socket));
下面是ClientSocket类中接收消息以及消息处理的代码过程:
@Slf4j
@Data
@Component
public class ClientSocket implements Runnable {
private Socket socket;
private DataInputStream inputStream;
private DataOutputStream outputStream;
private String clientId; // 客户端ID
public ClientSocket(Socket socket) {
this.socket = socket;
try {
this.inputStream = new DataInputStream(socket.getInputStream());
this.outputStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
log.error("Error creating input/output stream for client socket", e);
}
}
public ClientSocket() {
}
@Override
public void run() {
try {
byte[] buffer = new byte[1024];
int bytesRead;
//下面这个while循环中就是用来专门接收信息并写业务逻辑的,下面是我根据自己项目写的逻辑代码
while ((bytesRead = inputStream.read(buffer)) != -1) {
byte[] data = new byte[bytesRead];
System.arraycopy(buffer, 0, data, 0, bytesRead);
//校验数据完整性
System.out.println("data:" + data.length + "::" + Arrays.toString(data));
Boolean aBoolean = checkCode(data);
if (!aBoolean) {
log.info("数据校验失败,未知数据");
continue;
}
// 根据消息内容生成客户端ID
if (data.length > 7) {
//用站点名称作为key
try {
byte siteName = data[7];
String description = SiteEnum.find(Crc16Util.byteTo16String(siteName).trim()).getDescription();
clientId = description;
SocketPool.add(this);
SocketPool.remove(socket.toString());
} catch (NullPointerException e) {
log.info("未找到此站点");
e.printStackTrace();
}
}
GetAndSendMessage.tcpMessage = data;
log.info("收到设备{}的消息:{}", this.getClientId(), Arrays.toString(data));
Map<String, ClientSocket> stringClientSocketMap = get();
log.info("当前所有在线设备:{}", stringClientSocketMap.size());
for (Map.Entry<String, ClientSocket> entry : stringClientSocketMap.entrySet()) {
ClientSocket clientSocket1 = entry.getValue();
System.out.println(entry.getKey());
}
int activeCount = ((ThreadPoolExecutor) ReservoirApplication.threadPool).getActiveCount();
log.info("活跃中的线程数量:{}", activeCount);
int size = ((ThreadPoolExecutor) ReservoirApplication.threadPool).getQueue().size();
log.info("排队中的线程数量:{}", size);
int poolSize = ((ThreadPoolExecutor) ReservoirApplication.threadPool).getPoolSize();
log.info("当前总线程数量:{}", poolSize);
String s = ReservoirApplication.threadPool.toString();
log.info("线程池总览:{}", s);
// 在这里根据接收到的数据进行业务处理
GetAndSendMessage.getInfo();
outputStream.flush();
System.out.println("=========================================");
}
} catch (SocketException e) {
//清理一下离线设备
SocketHandler.clear();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 客户端断开连接时执行下面的代码
//判断一下clientId是不是空的,如果是空的说明没有说话,设备集合中的key存放的就是socket.toString
if (Objects.isNull(clientId)) {
SocketPool.remove(socket.toString());
System.out.println("设备 " + socket.toString() + " 下线");
log.info("当前剩余设备");
Map<String, ClientSocket> stringClientSocketMap = get();
for (Map.Entry<String, ClientSocket> entry : stringClientSocketMap.entrySet()) {
ClientSocket clientSocket1 = entry.getValue();
System.out.println(entry.getKey() + "::" + clientSocket1);
}
} else {
SocketPool.remove(clientId);
System.out.println("设备 " + clientId + " 下线");
log.info("当前剩余设备");
Map<String, ClientSocket> stringClientSocketMap = get();
for (Map.Entry<String, ClientSocket> entry : stringClientSocketMap.entrySet()) {
ClientSocket clientSocket1 = entry.getValue();
System.out.println(entry.getKey() + "::" + clientSocket1);
}
}
socket.close();
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里大概就是整个通讯的过程了,由于第一次写,代码实在太乱了,也没有整理,有看不懂的可以在留言,我持续改进。