TCP连接(Netty)
启动类增加
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)的更多相关文章
- 支持并发的httpclient(基于tcp连接池以及netty)
闲来无事,将曾经自己写的一个库放出来吧. . 有的时候会有这样子的需求: (1)serverA通过HTTP协议来訪问serverB (2)serverA可能会并发的像B发送非常多HTTP请求 类似于上 ...
- 我为 Netty 贡献源码 | 且看 Netty 如何应对 TCP 连接的正常关闭,异常关闭,半关闭场景
欢迎关注公众号:bin的技术小屋,本文图片加载不出来的话可查看公众号原文 本系列Netty源码解析文章基于 4.1.56.Final版本 写在前面..... 本文是笔者肉眼盯 Bug 系列的第三弹,前 ...
- TCP连接的建立和终止
TCP的简要要说明 标签(空格分隔): TCP 网络编程 Linux 面试 在此输入正文 一.TCP是什么 TCP全称传输控制协议(Transmission Control Protocol).TCP ...
- 简述TCP连接的建立与释放(三次握手、四次挥手)
在介绍TCP连接的建立与释放之前,先回顾一下相关知识. TCP是面向连接的运输层协议,它提供可靠交付的.全双工的.面向字节流的点对点服务.HTTP协议便是基于TCP协议实现的.(虽然作为应用层协议,H ...
- HTTP的RST包与WinHttp延迟关闭TCP连接
一.RST包也常见于断开TCP连接 几个月前用wireshark抓HTTP包发现有的网络通信在结束的时候没有使用四次握手,而是直接使用RST包.如: 在TCP协议中RST表示复位,用来异常的关闭连接 ...
- 一个完整的TCP连接
当我们向服务器发送HTTP请求,获取数据.修改信息时,都需要建立TCP连接,包括三次握手,四次分手. 什么是TCP连接? 为实现数据的可靠传输,TCP要在应用进程间建立传输连接.它是在两个传输用户之间 ...
- 查看 Apache并发请求数及其TCP连接状态
查看 Apache并发请求数及其TCP连接状态 (2011-06-27 15:08:36) 服务器上的一些统计数据: 1)统计80端口连接数 netstat -nat|grep -i "80 ...
- tcp连接listen的backlog剖析
TCP连接中,最重要的是连接TCP连接上,两个方向之间的各个状态及各个系统调用与状态之间的关系.往往可以以两种图表示,第一种是状态转换图,第二种是连接时序图.如下: 状态图: 时序图: ...
- 计算机网络(11)-----TCP连接的建立和释放
TCP连接的建立和释放 概述 TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程,运输连接有三个阶段:连接建立,数据传送和连接释放. TCP连接的建立 如图所示,假定A主机是客户端程序, ...
- 查看 并发请求数及其TCP连接状态【转】
服务器上的一些统计数据: 1)统计80端口连接数netstat -nat|grep -i "80"|wc -l 2)统计httpd协议连接数ps -ef|grep httpd|wc ...
随机推荐
- Linux Bridge和Tap关系详解
本文分享自天翼云开发者社区<Linux Bridge和Tap关系详解>,作者:x****n Linux Bridge介绍 Bridge(桥)是Linux上用来做TCP/IP二层协议交换的设 ...
- linux mint安装触控板手势fusuma
安装必要的包,终端输入: sudo apt-get install libinput-tools sudo apt-get install xdotool sudo gem install fusum ...
- Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]
Paper-cutting:思维很好,但代码很构式的 manacher 题. 蒟蒻 2025 年切的第一道题,是个紫,并且基本独立想出的,特此纪念. 判断能否折叠 我们先考虑一部分能折叠需要满足什么条 ...
- Java8 使用 stream().filter()过滤List对象等各种操作
内容简介 本文主要说明在Java8及以上版本中,使用stream().filter()来过滤一个List对象,查找符合条件的对象集合. list.stream().mapToDouble(User:: ...
- 搭建本地NCBI病毒库用于Blast
搭建本地NCBI病毒库用于Blast 目的:为了通过Blast剔除我数据集中所有与Human任意片段相似度超过97%的序列 日期:2022/11/17 1. Nt库下载 创建conda环境 conda ...
- Ai 系列 —— DeepSeek 初步介绍
DeepSeek 初步使用介绍 背景 Ai 正在慢慢在改变我们的生活,比如老一辈可能已经在用豆包(字节跳动推出的AI聊天机器人) 前端开发,某些公司内部已在使用图生文(设计稿生成前端代码) 网上也有许 ...
- linux tmux 使用教程
前言 Tmux 是一个终端复用器(terminal multiplexer),非常有用,属于常用的开发工具. 本文介绍如何使用 Tmux. 一.Tmux 是什么? 1.1 会话与进程 命令行的典型使用 ...
- golang 使用goto进行多错误处理
goto 语句介绍 在 Go 语言中,可以通过goto语句跳转到标签,进行代码间的无条件跳转.另外,goto语句在快速跳出循环.避免重复退出方面可以简化代码实现过程,但在结构化程序设计中一般不主张使用 ...
- JDK各个版本发布时间和版本名称
版权 版本 名称 发行日期 JDK 1.0 Oak(橡树) 1996-01-23 JDK 1.1 1997-02-19 JDK 1.1.4 Sparkler(宝石) 1997-09-12 JDK ...
- 实现领域驱动设计 - 使用ABP框架 - 领域逻辑 & 应用逻辑
领域逻辑 & 应用逻辑 如前所述,领域驱动设计中的业务逻辑分为两部分(层):领域逻辑和应用逻辑: 领域逻辑由系统的核心领域规则组成,应用逻辑实现应用特定的用例 虽然定义很明确,但实现起来可能并 ...