netty版本
<!--netty-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.31.Final</version>
</dependency>
本代码主要包含4个类:
1.ClientCache存储建立的连接;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ClientCache {
public static ConcurrentMap<String ,NioClient> clientMap=new ConcurrentHashMap<String,NioClient>();
}
2.NioClient 建立连接,断线重连;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class NioClient implements Runnable{
private final static Logger logger = LogManager.getLogger(NioClient.class);
private String host;
private int port;
private SocketChannel socketChannel;
private EventLoopGroup group;
private volatile boolean isChannelPrepared =false;
private volatile boolean isClose=false;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public NioClient(String host ,int port){
this.host=host;
this.port=port;
}
public SocketChannel getSocketChannel() {
return socketChannel;
}
public EventLoopGroup getGroup() {
return group;
}
public void connect(String host, Integer port) throws Exception {
// synchronized (NioClient.class){
if (!isChannelPrepared) {
//配置服务端的NIO线程组
group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group) // 绑定线程池
.channel(NioSocketChannel.class)
//.option(ChannelOption.SO_BACKLOG, 1024)
//.option(ChannelOption.SO_KEEPALIVE, true) // 2小时无数据激活心跳机制
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ClientChannelInitializer());
ChannelFuture f = b.connect(host, port).sync();
f.addListener(new GenericFutureListener<Future<? super Void>>() {
@Override
public void operationComplete(Future<? super Void> future) {
if (future.isSuccess()) {
isChannelPrepared = true;
socketChannel = (SocketChannel) f.channel();
logger.info("与服务器{}:{}连接建立成功...", host, port);
} else {
isChannelPrepared = false;
logger.error("与服务器{}:{}连接建立失败...", host, port);
// throw new NullPointerException();
}
}
});
// 关闭服务器通道
f.channel().closeFuture().sync();
} catch (Exception e) {
isChannelPrepared = false;
// e.printStackTrace();
logger.error("与服务器{}:{}连接出现异常...", host, port);
} finally {
isChannelPrepared=false;
group.shutdownGracefully();
if (!isClose) {
logger.info("与服务器{}:{}连接已断开, {}秒后重连...", host, port,60);
}
}
}
// }
}
/**
* 断线重连
*/
public void reConnect() {
reConnect(host, port);
}
private void reConnect(String host, int port) {
try {
isClose=false;
connect(host, port);
} catch (Exception e) {
isChannelPrepared = false;
log.error(e.getMessage(), e);
}
}
public void restart(){
logger.info("客户端重启与服务器{}:{}的连接...", host, port);
isChannelPrepared=false;
isClose=false;
group.shutdownGracefully();
reConnect()
System.out.println("重启!");
}
public void close(){
logger.info("客户端关闭与服务器{}:{}的连接...", host, port);
isChannelPrepared=false;
this.isClose=true;
for (String key :ClientCache.clientMap.keySet()) {
if (ClientCache.clientMap.get(key).getSocketChannel()==socketChannel) {
ClientCache.clientMap.remove(key);
}
}
group.shutdownGracefully();
group=null;
socketChannel=null;
logger.info("客户端关闭与服务器{}:{}的连接!", host, port);
}
public boolean writeAndFlush(String data){
logger.trace("向服务器{}:{}发送数据:{}", host, port,data);
boolean b = false;
if( isChannelPrepared) {
socketChannel.writeAndFlush(data);
b = true;
}else{
logger.error("与服务器{}:{}连接断开,无法发送数据[{}]...", host, port,data);
// throw new RuntimeException();
}
return b;
}
@Override
public void run() {
try {
this.isClose=false;
connect(host,port);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
NioClient nc= new NioClient("127.0.0.1",9000);
Thread t= new Thread(nc);
t.start();
int j=0;
while(j<5){
j++;
System.out.println(j);
if(j%2==0){
nc.writeAndFlush("test"+j+"\r\n");
}
}
nc.close();
}
}
3.ClientChannelInitializer 通道初始化配置;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) {
ChannelPipeline pipeline = socketChannel.pipeline();
/* LineBasedFrameDecoder的工作原理是:依次遍历ByteBuf中的可读字节,
判断看其是否有”\n” 或 “\r\n”, 如果有就以此位置为结束位置。
从可读索引到结束位置的区间的字节就组成了一行。 它是以换行符为结束标志的解码器,
支持携带结束符和不带结束符两种解码方式,同时支持配置单行的最大长度,
如果读到了最大长度之后仍然没有发现换行符,则抛出异常,同时忽略掉之前读到的异常码流。*/
pipeline.addLast(new LineBasedFrameDecoder(10010));
//字符串解码和编码
//LineBasedFrameDecoder + StringDecoder 就是一个按行切换的文本解码器。
pipeline.addLast( new StringDecoder());
pipeline.addLast( new StringEncoder());
//服务器的逻辑
pipeline.addLast("handler", new NioClientHandler());
}
}
4.NioClientHandler 接收到服务端数据处理,spring注入service;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class NioClientHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger logger = LogManager.getLogger(NioClientHandler.class);
private static NioClientHandler nioClientHandler;
@Autowired
private DataService dataService; //你自己service
public NioClientHandler() { }
@PostConstruct
public void init() {
nioClientHandler = this;
nioClientHandler.dataService = this.dataService;
}
/**
*客户端接收消息
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
new Thread(new Runnable() {
@Override
public void run() {
//接收到服务端发来的数据,业务处理
}}) .start();
}
/*
* 读取完毕 服务端发送过来的数据之后的操作
*/
/* @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务端接收数据完毕..");
// ctx.channel().write("");
// ctx.channel().flush();
}*/
/**
* 连接关闭!
* */
@Override
public void channelInactive(ChannelHandlerContext ctx) {
logger.error(" {}连接关闭!",ctx.channel().localAddress().toString() );
super.channelInactive(ctx);
SocketChannel channel= (SocketChannel) ctx.pipeline().channel();
for (String key :ClientCache.clientMap.keySet()) {
if (ClientCache.clientMap.get(key).getSocketChannel()==channel) {
ClientCache.clientMap.get(key).isChannelPrepared=false;
}
}
ctx.close();
// removeChannnelMap(ctx);
// 关闭流
}
/**
* 客户端主动连接服务端
* */
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// logger.info("RemoteAddress"+ ctx.channel().remoteAddress() + " active !");
// logger.info("msg"+ ctx.m + " active !");
// ctx.writeAndFlush("连接成功!");
super.channelActive(ctx);
}
/**
* 发生异常处理
* */
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
// logger.error("连接异常,连接异常:"+ DateUtils.dateToString(new Date())+cause.getMessage(), cause);
ctx.fireExceptionCaught(cause);
// removeChannnelMap(ctx);
ctx.close();
}
/* *//**
*删除map中ChannelHandlerContext
* *//*
private void removeChannnelMap(ChannelHandlerContext ctx){
*//* for( String key :NioServer.map.keySet()){
if( NioServer.map.get(key)!=null && NioServer.map.get(key).equals( ctx)){
NioServer.map.remove(key);
}
}*//*
}*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
super.userEventTriggered(ctx, evt);
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state().equals(IdleState.READER_IDLE))
ctx.close();
//标志该链接已经close 了
}
}
public static void main(String[] args) {
}
}
6.启动netty客户端
NioClient nc = new NioClient("127.0.0.1", "8000");
new Thread(nc).start();
ClientCache.clientMap.put("id", nc);
7.启动定时任务每分钟检查连接是否正常(定时器请自己配置或实现)
@Scheduled(cron = "00 */01 * * * ?") // 每分钟执行一次
public void nioTask() {
new Thread(()->{
try {
System.out.println("----------断线重连定时任务str-----------");
for (String key : ClientCache.clientMap.keySet()) {
if (!ClientCache.clientMap.get(key).isChannelPrepared &&
! ClientCache.clientMap.get(key).isClose) {
ClientCache.clientMap.get(key).reConnect();
}
};
System.out.println("===========断线重连定时任务end=========");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
8.发送数据
ClientCache.clientMap.get("id").writeAndFlush("id=123456;value=123\r\n");