启动类增加

public static void main(String[] args) {
SpringApplication application = new SpringApplication(CampusSecurityApplication.class);
application.run(args);
protocolThreadRun();
}
//服务端的,开启一个端口监听
private static void protocolThreadRun() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = CampusSecurityApplication.threadPoolExecutor();
threadPoolTaskExecutor.submit(() ->
TcpProtocolServer.tcpServer(8399, new ElectrictyTcpServerHandler()));
} @Bean
public static ThreadPoolTaskExecutor threadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(100);
executor.setMaxPoolSize(300);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("executorService-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setKeepAliveSeconds(120);
executor.initialize();
return executor;
}

监听

package ***;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j; /**
* @author
* @title ElectrictyTcpServerHandler
* @date 2023/6/1 18:48
* @description TODO
*/
@ChannelHandler.Sharable
@Slf4j
public class ElectrictyTcpServerHandler extends ChannelInboundHandlerAdapter { @Override
public void channelActive(ChannelHandlerContext ctx) {
log.info("【电表】客户端连接通道建立完成:" + ctx.channel().remoteAddress());
ChannelMap.addChannel("1001", ctx.channel());
} @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
log.info("【电表】========断开连接========" + ctx.channel());
log.error("【电表】========断开连接========" + ctx.channel());
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String receiveData = MessageUtil.readTcpMessage((ByteBuf) msg);
log.info("【电表】读取到电表客户端数据:" + receiveData);
//获取唯一标识
receiveData = receiveData.replace(" ", "");
String snno = receiveData.substring(receiveData.indexOf("68") + 2, receiveData.indexOf("68") + 14);
//put返回的值
ChannelMap.dataMap.put(snno, receiveData);
//通道put
ChannelMap.addChannel(snno, ctx.channel());
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// 处理客户端异常
ctx.close();
log.info("【电表】连接异常了");
}
}

ChannelMap类

package ***;

import ***;
import ***;
import io.netty.channel.Channel;
import io.netty.channel.socket.DatagramPacket; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* @author
* @date 2021/5/31 15:11
*/
public class ChannelMap { protected static Map<String, String> dataMap = new HashMap<>(); private static ConcurrentHashMap<String, Channel> channelHashMap = null; private static ConcurrentHashMap<String, DatagramPacket> packetHashMap = null; public static ConcurrentHashMap<String, Channel> getChannelHashMap() {
return channelHashMap;
} public static ConcurrentHashMap<String, DatagramPacket> getPacketHashMap() {
return packetHashMap;
} public static void addChannel(String key, Channel channel) {
if (channelHashMap == null) {
channelHashMap = new ConcurrentHashMap<>(10);
}
channelHashMap.put(key, channel);
} public static Channel getChannel(String channelKey) {
String key = channelKey;
ConcurrentHashMap<String, Channel> channelHashMap = getChannelHashMap();
if (!channelHashMap.containsKey(key)) {
//不包含的话赋值初始值
key = "1001";
}
Channel channel = channelHashMap.get(key);
if (channel == null || !channel.isActive()) {
ChannelMap.getChannelHashMap().remove(key);
throw new BusinessException(ResultCode.DATA_ERROR, "连接中断");
}
return channel;
} public static DatagramPacket getPacket(String key) {
ConcurrentHashMap<String, DatagramPacket> packetHashMap = getPacketHashMap();
return packetHashMap.get(key);
} }

MessageUtil

package ***;

import ***;
import ***;
import ***;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j; /**
* @author
* @title MessageUtil
* @date 2023/6/1 18:53
* @description TODO
*/
@Slf4j
public class MessageUtil {
/**
* 发送tcp消息
*
* @param channelKey 通道标识
* @param message 要发送的消息
* @param timeout 超时时间,范围,秒
* @param messageType 发送的消息类型
* @return 返回客户端回送的消息
*/
public static String sendTcpMessageAddListener(String channelKey, String message, Integer timeout, MessageType messageType) {
Channel channel = ChannelMap.getChannel(channelKey);
ByteBuf byteBuf = writeMessageToByteBuff(message, messageType);
ChannelFuture channelFuture = channel.writeAndFlush(byteBuf);
return messageListener(channelFuture, channelKey, timeout);
} /**
* 发送tcp消息,不监听返回
*
* @param channelKey
* @param message
* @param messageType
* @date 2023/6/2 16:34
* @author hhs
*/
public static void sendTcpMessage(String channelKey, String message, MessageType messageType) {
Channel channel = ChannelMap.getChannel(channelKey);
ByteBuf byteBuf = writeMessageToByteBuff(message, messageType);
ChannelFuture channelFuture = channel.writeAndFlush(byteBuf);
messageListenerStatus(channelFuture,channelKey);
} private static void messageListenerStatus(ChannelFuture channelFuture,String channelKey) {
channelFuture.addListener((ChannelFutureListener) future -> {
if (!future.isSuccess()) {
throw new BusinessException(ResultCode.OPERATION_FAILURE, "发送消息到客户端失败!");
} else {
log.info("=========成功发送消息到客户端==========");
}
});
String data = ChannelMap.dataMap.get(channelKey);
if (StringUtil.isNotEmpty(data)) {
ChannelMap.dataMap.remove(channelKey);
}
} public static String sendUdpMessageAddListener(String key, String message, Integer timeout, MessageType messageType) {
Channel channel = ChannelMap.getChannel(key);
DatagramPacket packet = ChannelMap.getPacket(key);
ByteBuf byteBuf = writeMessageToByteBuff(message, messageType);
DatagramPacket datagramPacket = new DatagramPacket(byteBuf, packet.sender());
ChannelFuture channelFuture = channel.writeAndFlush(datagramPacket);
return messageListener(channelFuture, key, timeout);
} private static ByteBuf writeMessageToByteBuff(String message, MessageType messageType) {
ByteBuf buff = Unpooled.buffer();
switch (messageType) {
case HEX:
return buff.writeBytes(BinaryUtil.hexString2Bytes(message));
case STRING:
return buff.writeBytes(message.getBytes(CharsetUtil.UTF_8));
default:
return Unpooled.copiedBuffer(message, CharsetUtil.UTF_8);
}
} /**
* 消息监控
*
* @param channelFuture channel监控线程
* @param channelKey ChannelMap中对应哪个的key
* @param timeout 超时时间 单位:秒
* @return 返回客户端返回的消息
*/
private static String messageListener(ChannelFuture channelFuture, String channelKey, Integer timeout) {
long startTime = System.currentTimeMillis();
channelFuture.addListener((ChannelFutureListener) future -> {
if (!future.isSuccess()) {
throw new BusinessException(ResultCode.OPERATION_FAILURE, "发送消息到客户端失败!");
} else {
log.info("=========成功发送消息到客户端==========");
}
});
String data;
while (true) {
long endTime = System.currentTimeMillis();
data = ChannelMap.dataMap.get(channelKey);
if (StringUtil.isNotEmpty(data)) {
ChannelMap.dataMap.remove(channelKey);
break;
}
if (endTime - startTime > timeout * 1000) {
throw new BusinessException(ResultCode.FAILURE, "等待超时!");
}
}
return data;
} public static String readTcpMessage(ByteBuf msg) {
byte[] bytes = new byte[msg.readableBytes()];
msg.readBytes(bytes);
String message = BinaryUtil.bytesToHex(bytes);
msg.release();
return message;
}
}

TcpProtocolServer类

package ***;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j; /**
* @author
* @date 2021/5/31 13:34
*/
@Slf4j
public class TcpProtocolServer { private TcpProtocolServer() {
} public static void tcpServer(Integer port, ChannelHandler channelHandler) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(8);
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(channelHandler);
}
});
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
ChannelFuture cf = bootstrap.bind(port).sync();
cf.channel().closeFuture().sync();
} catch (Exception e) {
log.error("tcp服务端启动失败,端口号:" + port + "错误消息:" + e.getMessage());
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} }

BinaryUtil类,转换16进制等

package ***;

/**
* 字节转换工具类
*
* @author admin
*/
public class BinaryUtil {
/**
* 字节数组转16进制
*
* @param bytes 需要转换的byte数组
* @return 转换后的Hex字符串
*/
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
String hex = Integer.toHexString(aByte & 0xFF);
if (hex.length() < 2) {
sb.append(0);
}
sb.append(hex);
}
return sb.toString().toUpperCase();
} /**
* hexString2Bytes
* 16进制字符串转字节数组
*
* @param src 16进制字符串
* @return 字节数组
*/
public static byte[] hexString2Bytes(String src) {
int l = src.length() / 2;
byte[] ret = new byte[l];
for (int i = 0; i < l; i++) {
ret[i] = Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
}
return ret;
}
}

TCP连接(Netty)的更多相关文章

  1. 支持并发的httpclient(基于tcp连接池以及netty)

    闲来无事,将曾经自己写的一个库放出来吧. . 有的时候会有这样子的需求: (1)serverA通过HTTP协议来訪问serverB (2)serverA可能会并发的像B发送非常多HTTP请求 类似于上 ...

  2. 我为 Netty 贡献源码 | 且看 Netty 如何应对 TCP 连接的正常关闭,异常关闭,半关闭场景

    欢迎关注公众号:bin的技术小屋,本文图片加载不出来的话可查看公众号原文 本系列Netty源码解析文章基于 4.1.56.Final版本 写在前面..... 本文是笔者肉眼盯 Bug 系列的第三弹,前 ...

  3. TCP连接的建立和终止

    TCP的简要要说明 标签(空格分隔): TCP 网络编程 Linux 面试 在此输入正文 一.TCP是什么 TCP全称传输控制协议(Transmission Control Protocol).TCP ...

  4. 简述TCP连接的建立与释放(三次握手、四次挥手)

    在介绍TCP连接的建立与释放之前,先回顾一下相关知识. TCP是面向连接的运输层协议,它提供可靠交付的.全双工的.面向字节流的点对点服务.HTTP协议便是基于TCP协议实现的.(虽然作为应用层协议,H ...

  5. HTTP的RST包与WinHttp延迟关闭TCP连接

    一.RST包也常见于断开TCP连接  几个月前用wireshark抓HTTP包发现有的网络通信在结束的时候没有使用四次握手,而是直接使用RST包.如: 在TCP协议中RST表示复位,用来异常的关闭连接 ...

  6. 一个完整的TCP连接

    当我们向服务器发送HTTP请求,获取数据.修改信息时,都需要建立TCP连接,包括三次握手,四次分手. 什么是TCP连接? 为实现数据的可靠传输,TCP要在应用进程间建立传输连接.它是在两个传输用户之间 ...

  7. 查看 Apache并发请求数及其TCP连接状态

    查看 Apache并发请求数及其TCP连接状态 (2011-06-27 15:08:36) 服务器上的一些统计数据: 1)统计80端口连接数 netstat -nat|grep -i "80 ...

  8. tcp连接listen的backlog剖析

    TCP连接中,最重要的是连接TCP连接上,两个方向之间的各个状态及各个系统调用与状态之间的关系.往往可以以两种图表示,第一种是状态转换图,第二种是连接时序图.如下: 状态图: 时序图:         ...

  9. 计算机网络(11)-----TCP连接的建立和释放

    TCP连接的建立和释放 概述 TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程,运输连接有三个阶段:连接建立,数据传送和连接释放. TCP连接的建立 如图所示,假定A主机是客户端程序, ...

  10. 查看 并发请求数及其TCP连接状态【转】

    服务器上的一些统计数据: 1)统计80端口连接数netstat -nat|grep -i "80"|wc -l 2)统计httpd协议连接数ps -ef|grep httpd|wc ...

随机推荐

  1. Linux Bridge和Tap关系详解

    本文分享自天翼云开发者社区<Linux Bridge和Tap关系详解>,作者:x****n Linux Bridge介绍 Bridge(桥)是Linux上用来做TCP/IP二层协议交换的设 ...

  2. linux mint安装触控板手势fusuma

    安装必要的包,终端输入: sudo apt-get install libinput-tools sudo apt-get install xdotool sudo gem install fusum ...

  3. Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]

    Paper-cutting:思维很好,但代码很构式的 manacher 题. 蒟蒻 2025 年切的第一道题,是个紫,并且基本独立想出的,特此纪念. 判断能否折叠 我们先考虑一部分能折叠需要满足什么条 ...

  4. Java8 使用 stream().filter()过滤List对象等各种操作

    内容简介 本文主要说明在Java8及以上版本中,使用stream().filter()来过滤一个List对象,查找符合条件的对象集合. list.stream().mapToDouble(User:: ...

  5. 搭建本地NCBI病毒库用于Blast

    搭建本地NCBI病毒库用于Blast 目的:为了通过Blast剔除我数据集中所有与Human任意片段相似度超过97%的序列 日期:2022/11/17 1. Nt库下载 创建conda环境 conda ...

  6. Ai 系列 —— DeepSeek 初步介绍

    DeepSeek 初步使用介绍 背景 Ai 正在慢慢在改变我们的生活,比如老一辈可能已经在用豆包(字节跳动推出的AI聊天机器人) 前端开发,某些公司内部已在使用图生文(设计稿生成前端代码) 网上也有许 ...

  7. linux tmux 使用教程

    前言 Tmux 是一个终端复用器(terminal multiplexer),非常有用,属于常用的开发工具. 本文介绍如何使用 Tmux. 一.Tmux 是什么? 1.1 会话与进程 命令行的典型使用 ...

  8. golang 使用goto进行多错误处理

    goto 语句介绍 在 Go 语言中,可以通过goto语句跳转到标签,进行代码间的无条件跳转.另外,goto语句在快速跳出循环.避免重复退出方面可以简化代码实现过程,但在结构化程序设计中一般不主张使用 ...

  9. JDK各个版本发布时间和版本名称

    版权 版本 名称 发行日期 JDK 1.0 Oak(橡树) 1996-01-23 JDK 1.1   1997-02-19 JDK 1.1.4 Sparkler(宝石) 1997-09-12 JDK ...

  10. 实现领域驱动设计 - 使用ABP框架 - 领域逻辑 & 应用逻辑

    领域逻辑 & 应用逻辑 如前所述,领域驱动设计中的业务逻辑分为两部分(层):领域逻辑和应用逻辑: 领域逻辑由系统的核心领域规则组成,应用逻辑实现应用特定的用例 虽然定义很明确,但实现起来可能并 ...