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 ...
随机推荐
- 天翼云CDN全站加速产品对websocket协议的支持
本文分享自天翼云开发者社区<天翼云CDN全站加速产品对websocket协议的支持>,作者:郭****迎 1.背景介绍 HTTP 协议有一个缺陷:通信只能由客户端发起.这种单向请求的特点, ...
- mount命令及挂载本地yum源
mount命令 mount [-t vfstype] [-o options] device dir 其中: 1.-t vfstype 指定文件系统的类型,通常不必指定.mount 会自动选择正确的类 ...
- THUWC2025 游记
Day -C 先进入金国大臣面积群,然后发现 xyf 又在行联考学生群故事. Day -1 早上赶飞机进京.飞机上启动钢丝.到达大兴机场之后坐火车前往北京西站,然后坐地铁到海淀黄庄.非常饿,但是决定先 ...
- Linux编译方式安装redis
redis安装1.安装gcc-c++编译环境yun install gcc-c++ 2.把redis上传到linux服务器 3.解压缩 tar -zxf redis-3.0.0.tar.gz 4. ...
- Shell脚本常用写法
一.变量定义 | 赋值 | 输出 1.debugmap #!/bin/bash source /etc/profile # hive_json_tuple_params_orignal.tmp # e ...
- Luogu P5089 元素周期表 / Codeforces 1012B Chemical table 题解 [ 并查集 ] [ 二分图 ] [ 图论建模 ] [ 棋盘覆盖问题 ]
双倍经验:Luogu P5089 元素周期表 ,CF1012B Chemical table:模拟赛搬的好题,有点厉害.赛时10min码的假贪心拿了五十多分,赢. 并查集思路 1 对于此类棋盘整行整列 ...
- 流程控制之增强for循环
语法 for (声明语句:表达式){ //代码语句} 实例: package com.yeyue.struct;public class ForDemo05 { public stati ...
- BurpSuite重放发包的一些区别
目录 Send Group in parallel Single connection 和 Separate connections 的差别 实际应用场景 Reference 2022年之后,Burp ...
- MacOS15+Xcode版本16+对ReactNative项目进行编译和上传到APPStore的踩坑记录
作者:Kovli 重要通知:红宝书第5版2024年12月1日出炉了,感兴趣的可以去看看,https://u.jd.com/saQw1vP 红宝书第五版中文版 红宝书第五版英文原版pdf下载(访问密码: ...
- 安川YASKAWA工业机器人板卡维修策略
一.安川YASKAWA工业机器人板卡识别故障症状 首先,需要准确识别电路板故障的症状.这通常包括安川YASKAWA机器人操作不稳定.错误代码频繁出现.某些功能失效或整体性能下降等.通过仔细观察和诊断, ...